java高階(十五)——多執行緒
java高階部分多執行緒
建立執行緒的方式
方式一:繼承Thread類
1.建立一個類繼承Thread類
2.在子類中重寫Thread類的run()方法
3.建立子類物件
4.通過子類物件呼叫start()方法 (start方法的作用:①啟動執行緒②呼叫當前執行緒的run方法)
方式二:實現Runnable介面(建議用這種)
1.建立一個類實現Runnable介面
2.實現類中實現介面的抽象方法
3.建立介面的實現類物件
5.通過Thread類的物件呼叫start()方法
方式一和方式二兩種方式的比較
聯絡
Thread類底層也是實現的Runnable介面
都需要重寫run方法
方式三:實現Callable介面(jdk1.5後新增)相比方式二更加強大
具體步驟:
1.建立一個類實現Callable介面
2.實現Callable介面中的call()方法
3.編寫執行緒具體操作程式碼
4.建立Callable介面的實現類物件
5.將Callable介面的實現類物件傳入FutureTask類的構造器中,
建立FutureTask類的物件
6.將FutureTask類的物件作為引數傳入Thread類的構造器中,
相比實現Runnable介面這種方式,實現Callable的方式有什麼強大之處?
1.call()方法中可以擁有返回值
2.該方式中可以處理異常
3.該方式中可以使用泛型
方式四:執行緒池
步驟
1.建立執行緒池大小
ExecutorService service = Executors.newFixedThreadPool(10);
2.執行執行緒
// execute 適用於實現Runnable介面
service.execute(new Runnable() {...}
// submit 適用於實現Callable介面
service.submit(new Callable<Object>() {...}
3.關閉執行緒池
service.shutdown();
執行緒 生命週期
新建
就緒
執行
阻塞
死亡
執行緒的同步
執行緒的安全問題
當一個執行緒在執行時,遇到了阻塞,這時另一個執行緒參與了進來,也進行操作,就會造成執行緒安全問題(如賣票)
如何解決
方式一:同步程式碼塊
synchronize(同步監視器){程式碼:操作共享資料的程式碼}
同步監視器(通俗講就叫鎖)
任何一個類的物件,都可以充當鎖(要求:多個執行緒必須共用同一把鎖)
也可以使用this代表當前物件,但是隻能在建立一個物件的情況下使用(思考)
缺點
操作同步程式碼塊時,只能有一個執行緒參與,其他執行緒需要等待,就相當於一個單執行緒了
方式二:同步方法
在建立方法時使用synchronized
使用實現Runnable介面的方式
只需要建立一個實現類的物件,因此可以直接使用synchronized修飾run()方法
此時,同步監聽器為:this
使用繼承Thread的方式
因為會建立多個子類的物件,直接使用synchronized修飾run方法不起作用,還需將這個方法使用static關鍵字修飾
此時,同步監聽器就變為:當前類本身
方式三:Lock鎖
java.util.concurrent.locks.Lock介面是控制多個執行緒對共享資源訪問的工具。
使用同步鎖:建立Lock介面的實現類ReentrantLock的物件
通過物件呼叫鎖定方法lock()/解鎖方法unlock()
synchronized與lock鎖的異同
相同點
都是解決執行緒的同步安全問題
區別
synchronized同步方法/程式碼塊的方式,每次執行完程式碼後自動解鎖
而lock鎖的方式需要手動鎖定、手動解鎖
執行緒的死鎖
不同的執行緒互相佔用了對方的資源不放棄,都在等待對方釋放,就形成了執行緒的死鎖(如兩個人吃飯,只有一雙筷子,都在等待對方將另一隻筷子給自己)
解決死鎖
演算法、減少同步資源的定義、避免巢狀同步資源
執行緒的通訊
wait()方法
當前執行緒進去阻塞狀態
notify()方法
喚醒被wait的一個執行緒(如果有多個執行緒被wait),喚醒優先順序較高的
notifyAll()方法
喚醒所有被wait的執行緒
這3個方法必須放在synchronized程式碼塊中,這三個方法的呼叫中必須是同步程式碼塊或同步方法的同步監視器
sleep()和wait()方法的區別
相同點
都可以讓當前執行緒進入阻塞狀態
不同點
1.宣告的位置:
sleep方法在Thread類中
wait方法在Object類中
2.呼叫位置
sleep方法可以在任何需要的地方呼叫
wait方法必須在同步程式碼塊/同步方法中
3.是否釋放同步監視器
sleep方法不釋放
wait方法釋放同步監視器