在調用wait()之前,線程必須獲得該對象的對象層級鎖,即只能在同步方法或同步塊中調用wait()方法。在執行wait()方法後,當前線程釋放鎖,從wait()停止執行。在從wait()返回前,線程與其它線程競爭重新獲得鎖。如果調用wait()時沒有持有適當的鎖,則拋出IllegalMonitorStateException。
方法notify()也要在同步方法或同步塊中調用,即在調用前,線程也必須獲得該對象的對象層級鎖。如果調用notify()時沒有持有適當的鎖,也會拋出IllegalMonitorStateException。該方法用來通知那些可能等待該對象的對象鎖的其它線程,如果有多個線程等待,則由線程規劃器隨機挑選出其中一個是wait狀態的線程,對其發出通知notify,並使它等待擷取該對象的對象鎖。需要說明的是,在執行notify()方法之後,當前線程不會馬上釋放該對象鎖,是wait狀態的線程也並不能馬上擷取該對象鎖,要等到執行notify()方法的線程將當前這次的語句程式執行完(如果notify在迴圈中的情況就會執行完當前這次迴圈語句塊中的語句再喚醒另外一個等待線程)當前線程才會釋放鎖,而wait狀態所在的線程才可以擷取該對象鎖。當第一個獲得該對象鎖的wait線程運行完畢之後,它會釋放掉該對象鎖,此時如果該對象沒有再次使用notify語句,則即便該對象已經空閑,其它wait狀態等待的線程由於沒有得到該對象的通知,還會繼續阻塞在wait狀態,直到這個對象發出一個notify或notifyAll。
紅色字型部分見程式碼分析與解析
package test;import java.util.ArrayList;import java.util.List;public class Test6{ private List<String>list=new ArrayList<String>(); public void add(){ list.add("草根"); } public int size(){return list.size(); } public static void main(String[] args) { final Test6 list=new Test6(); final Object lock=new Object(); Thread threadA=new Thread(){ public void run(){ try{ synchronized(lock){ if(list.size()!=5){ System.out.println("wait begin"+System.currentTimeMillis()); lock.wait();//wait()後面的語句都會停止執行釋放當前鎖 System.out.println("wait end"+System.currentTimeMillis()); } } }catch(Exception e){ } } }; threadA.start(); Thread threadB=new Thread(){ public void run(){ try{ synchronized(lock){ for(int i=0;i<10;i++){ list.add(); System.out.println("添加了"+(i+1)+"個元素"); if(list.size()==5){ lock.notify();//notify將會繼續執行完後面語句才會釋放當前鎖 System.out.println("我已經發出喚醒通知"); } Thread.sleep(1000); } } }catch(Exception e){ } } }; threadB.start(); }}
sleep和wait的區別
首先要明白多線程中鎖是對象層級的不是線程層級的,所以wait,notify,notifyall都只能在同步塊中對象(鎖)中調用,sleep線程對象(如Thread)調用
所以如果是調用Object中的方法wait方法那麼會導致阻塞,只有等待相應的對象調用了
notify或者notifyall之後才會變為可運行態重新獲得CPU執行時間,如果是單調用sleep方法是對鎖沒有影響的,因為sleep是Thread中的,sleep方法調用不會釋放鎖,但是wait方法就會釋放鎖,但是wait和sleep都會導致線程暫停讓出CPU執行權,OS會重新分配給其它線程。
sleep 讓線程從 【running】 -> 【阻塞態】 時間結束/interrupt -> 【runnable】
wait 讓線程從 【running】 -> 【等待隊列】notify -> 【鎖池】 -> 重新拿到鎖標識-》【runnable】