多執行緒併發詳解
一、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(); } } }); } }