一 需求
子執行緒中開啟一個主執行緒去獲取某些資料,此時子執行緒要處理等待狀態,獲取成功之後繼續執行子執行緒中之後的程式碼.
- 問題:
當開啟主執行緒去獲取資料的時候,子執行緒的程式碼也會順序去執行,這樣並不能等到主執行緒有結果的時候再去執行子執行緒中的程式碼. - 分析:
先來分析一下,當線上程A中開啟另外一個執行緒B的時候,執行緒A中的程式碼還是順序執行執行緒B的程式碼也會執行.這樣的話執行緒A中需要執行緒B中返回引數的方法就沒辦法正確執行. - 解決:
這裡就用到了執行緒的wiat() notify() 和 synchronized 關鍵字,先來看下解決方法.final Object obj = new Object(); new Thread(){ @Override public void run() { super.run(); //通過Handler建立一個主執行緒. new Handler(getMainLooper()).post(new Runnable() { @Override public void run() { synchronized (obj) { //模擬主執行緒耗時操作 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } str = "WangChao"; Log.e("CHAO", "run 2" + str); obj.notify(); } } }); Log.e("CHAO","run 1"); synchronized (obj) { try { obj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } //子執行緒需要主執行緒的返回引數 if (str != null) { Log.e("CHAO", "run 3" + str); } } } }.start();複製程式碼
執行可知先執行的是run 1 ,之後是主執行緒中的run 2 ,然後是子執行緒中的 run 3.這樣就可以達到我們想要的結果.執行結果如下:
com.example.wang.threaddemo E/CHAO: run 1 com.example.wang.threaddemo E/CHAO: run 2WangChao com.example.wang.threaddemo E/CHAO: run 3WangChao複製程式碼
二 涉及的Java執行緒的知識點
-
synchronized關鍵字的用法
被synchronized修飾的方法稱為同步方法,預設的鎖是當前物件.所謂同步方法是如果有一個執行緒正在訪問該方法的時候,其他的方法呼叫到該方法的時候將進入等待的狀態.當有多個同步方法的時候,一旦一個執行緒呼叫了其中的一個同步方法,便對當前物件加鎖,其他的執行緒呼叫擁有同物件鎖的其它同步方法時會處於等待的狀態,等之前的執行緒釋放掉物件鎖之後繼續執行.
1.去修飾方法 : 物件鎖預設是當前物件 this .public synchronized void method2(){ Log.e("CHAO", "method2: " + Thread.currentThread().getName()); }複製程式碼
還可以這樣寫:
public void method2(){ synchronized (this) { Log.e("CHAO", "method2: " + Thread.currentThread().getName()); } }複製程式碼
2.同步語句塊 : 可以自己去設定加鎖的物件.
synchronized (obj) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } str = "WangChao"; Log.e("CHAO", "2" + str); obj.notify(); }複製程式碼
這裡需要注意的是synchronized (obj) 裡面的引數,也就是要給那個物件加鎖.這個物件必須是唯一的切多個執行緒都能拿到這個物件.(再使用Stirng最為物件鎖的時候一定要注意String的重新賦值,一旦賦值改物件就改變了不再是我們之前加鎖的物件),通常可以自己去建立一個final 的 Object類去加鎖,也可以使用this,Class物件等.
3.同步靜態方法啊: 加鎖物件是當前的Class物件.
public static synchronized void method1(){ int count = 0; for (int i = 0; i < 120000; i++) { count++; } Log.e("CHAO", "method1: " + Thread.currentThread().getName()); }複製程式碼
同不得靜態方法再多執行緒中的呼叫和非靜態的是一致的.
-
wait()
首先wait()是屬於Object類的方法,從原始碼給出的解釋來看,wait()方法可以做到如下幾點:
(1)首先,呼叫了wait()之後會引起當前執行緒處於等待狀狀態。
(2)其次,每個執行緒必須持有該物件的monitor(監視)。如果在當前執行緒中調 用wait()方法之後,該執行緒就會釋放monitor的持有物件並讓自己處於等 待狀態。
(3)如果想喚醒一個正在等待的執行緒,那麼需要開啟一個執行緒通過notify()或者notifyAll()方法去通知正在等待的執行緒獲取monitor物件。如此,該執行緒即可打破等待的狀態繼續執行程式碼.注意: wait()方法要使用在synchronized修飾的方法裡面要不然會報異常,並且是synchronized()加鎖的那個物件呼叫該方法.異常如下:
java.lang.IllegalMonitorStateException: object not locked by thread before wait()複製程式碼
-
notify() , nitifyAll()
notify()是屬於Object類的方法.notify喚醒一個在這個物件上等待的執行緒監控。如果有任何執行緒在等待這個物件,其中一個執行緒被選擇被喚醒。這個選擇是任意的,並且發生在執行的自由裁量。一個執行緒在一個物件上等待通過呼叫wait()等方法來監視。nitifyAll喚醒所有的再等待中的執行緒.注意:nitify喚醒的執行緒會在該執行緒的wiat之後繼續執行,特別要注意的是呼叫wiat的同步語句塊的物件鎖要和nitify的同步語句塊的物件鎖是同一個,否則的話你是喚醒不了wait狀態的.
final Object obj = new Object(); new Thread(){ @Override public void run() { super.run(); new Handler(getMainLooper()).post(new Runnable() { @Override public void run() { //注意這個加鎖物件 synchronized (obj) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } str = "WangChao"; Log.e("CHAO", "2" + str); //注意喚醒方法的呼叫者 obj.notify(); } } }); Log.e("CHAO","run"+1); //注意這個加鎖物件 synchronized (obj) { try { //注意等待方法的呼叫者 obj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } if (str != null) { Log.e("CHAO", "3" + str); } } } }.start();複製程式碼
- sleep()
sleep()方法來自於Thread類,從原始碼給出的解釋來看,sleep()方法可以做到如下幾點:
(1)首先,呼叫sleep()之後,會引起當前執行的執行緒進入暫時中斷狀態,也即睡眠狀態。
(2)其次,雖然當前執行緒進入了睡眠狀態,但是依然持有monitor物件。
(3)在中斷完成之後,自動進入喚醒狀態從而繼續執行程式碼
總結
(1)線上程的執行過程中,呼叫該執行緒持有monitor物件的wait()方法時,該執行緒首先會進入等待狀態,並將自己持有的monitor物件釋放。
(2)如果一個執行緒正處於等待狀態時,那麼喚醒它的辦法就是開啟一個新的執行緒,通過notify()或者notifyAll()的方式去喚醒。當然,需要注意的一點就是,必須是同一個monitor物件。
(3)sleep()方法雖然會使執行緒中斷,但是不會將自己的monitor物件釋放,在中斷結束後,依然能夠保持程式碼繼續執行。
歡迎大家的點評和補充,喜歡的話就給個贊?支援下吧~