1. 程式人生 > 實用技巧 >多執行緒併發詳解

多執行緒併發詳解

一、Java 執行緒實現/建立方式

  注意:

  • 新建的執行緒不會自動開始執行,必須通過start( )方法啟動

  • 不能直接呼叫run()來啟動執行緒,這樣run()將作為一個普通方法立即執行,執行完畢前其他執行緒無法併發執行

  • Java程式啟動時,會立刻建立主執行緒,main就是在這個執行緒上執行。當不再產生新執行緒時,程式是單執行緒的

 1.1 繼承Thread 類

   Thread 類本質上是實現了 Runnable 介面的一個例項,代表一個執行緒的例項。啟動執行緒的唯一方法就是通過 Thread 類的 start()例項方法。start()方法是一個 native 方法,它將啟動一個新執行緒,並執行 run()方法。

    • 優勢:編寫簡單

    • 劣勢:無法繼承其它父類

  1.1.1建立:繼承Thread+重寫run

  1.1.2 啟動:建立子類物件+呼叫start

public class StartThread extends Thread{

	//執行緒入口點
	@Override
	public void run() {
		for(int i=0;i<10;i++) {
			System.out.println("listen music");
		}
	}
	public static void main(String[] args) {
		//建立子類物件
		StartThread st=new StartThread();
		//呼叫start方法
		st.start();//開啟新執行緒交於cpu決定執行順序
		for(int i=0;i<10;i++) {
			System.out.println("coding");
		}
	}
}

 1.2實現runnable介面

   如果自己的類已經 extends 另一個類,就無法直接 extends Thread,此時,可以實現一個Runnable 介面。

    • 優勢:可以繼承其它類,多執行緒可共享同一個Runnable物件

    • 劣勢:程式設計方式稍微複雜,如果需要訪問當前執行緒,需要呼叫Thread.currentThread()方法

   1.2.1建立:實現runnable介面+重寫run

   1.2.2 啟動:建立實現類物件+Thread類物件+呼叫start

public class StartRun implements Runnable{

	//執行緒入口點
	@Override
	public void run() {
		for(int i=0;i<10;i++) {
			System.out.println("listen music");
		}
	}
	public static void main(String[] args) {
		//建立實現類物件
		StartRun st=new StartRun();
		//建立代理類物件
          //啟動 MyThread,需要首先例項化一個 Thread,並傳入自己的 MyThread 例項:
		Thread t=new Thread(st); 
          //事實上,當傳入一個 Runnable target 引數給 Thread 後,Thread 的 run()方法就會呼叫target.run()
		//呼叫start方法
		t.start();//開啟新執行緒交於cpu決定執行順序
		
		//匿名法
//		new Thread(new StartRun()).start();
		
		for(int i=0;i<10;i++) {
			System.out.println("coding");
		}
	}
}

 1.3實現Callable介面

   有返回值的任務必須實現 Callable 介面,類似的,無返回值的任務必須 Runnable 介面。執行Callable 任務後,可以獲取一個 Future 的物件,在該物件上呼叫 get 就可以獲取到 Callable 任務返回的 Object 了,再結合線程池介面 ExecutorService 就可以實現傳說中有返回結果的多執行緒了。

    • 與實行Runnable相比, Callable功能更強大些

    • 方法不同

    • 可以有返回值,支援泛型的返回值

    • 可以丟擲異常

    • 需要藉助FutureTask,比如獲取返回結果

  Future介面

    • 可以對具體Runnable、Callable任務的執行結果進行取消、查詢是否完成、獲取結果等。

    • FutrueTask是Futrue介面的唯一的實現類

    • FutureTask 同時實現了Runnable, Future介面。它既可以作為Runnable被執行緒執行,又可以作為Future得到Callable的返回值
    //建立一個執行緒池
    ExecutorService pool = Executors.newFixedThreadPool(taskSize);
    // 建立多個有返回值的任務
    List<Future> list = new ArrayList<Future>(); 
    for (int i = 0; i < taskSize; i++) { 
        Callable c = new MyCallable(i + " "); 
        // 執行任務並獲取 Future 物件
        Future f = pool.submit(c); 
        list.add(f); 
    } 
    // 關閉執行緒池
    pool.shutdown(); 
    // 獲取所有併發任務的執行結果
    for (Future f : list) { 
    // 從 Future 物件上獲取任務的返回值,並輸出到控制檯
        System.out.println("res:" + f.get().toString()); 
    }

 1.4基於執行緒池的方式

   執行緒和資料庫連線這些資源都是非常寶貴的資源。那麼每次需要的時候建立,不需要的時候銷燬,是非常浪費資源的。那麼我們就可以使用快取的策略,也就是使用執行緒池

// 建立執行緒池
 ExecutorService threadPool = Executors.newFixedThreadPool(10);
 while(true) {
     threadPool.execute(new Runnable() { // 提交多個執行緒任務,並執行
         @Override
         public void run() {
             System.out.println(Thread.currentThread().getName() + " is running ..");
             try {
                 Thread.sleep(3000);
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }
      });
   } 
}