JAVA執行緒間協作wait、notify、notifyAll、sleep用途
在上節中,介紹了java多執行緒中同步鎖的概念,synchronized方法和synchronized程式碼塊都是為了解決執行緒併發的問題,同一時間允許一個執行緒訪問當前類或者物件。如果涉及到執行緒間的協作通訊,就需要用到wait、notify、notifyAll方法,這三個方法是Object的方法。也就是任何物件都具有這三個方法,在java中wait、notify、notifyAll要放在synchronized方法中或者synchronized程式碼塊中使用。從功能上來說,wait方法線上程獲得物件鎖之後,主動釋放掉物件鎖,並且休眠當前執行緒。直到有執行緒呼叫notify方法之後,他才會繼續獲得物件鎖執行當前執行緒。notify方法就是用來喚醒物件鎖的,但是notify執行完之後並不是馬上執行當前喚醒的執行緒,而是等當前執行緒正在執行的執行緒執行完之後再執行。wait方法和Thread的Sleep方法作用類似,都是暫停執行緒,釋放cpu使用權。區別在於wait方法會釋放掉鎖,而sleep只是休眠當前物件,並不會讓出物件鎖。呼叫了wait的執行緒會一直等待,後面程式碼不會再執行,直到有其他執行緒呼叫notify或者notifyAll才會繼續執行。notifyAll的作用是喚醒所有的休眠的執行緒,注意只是喚醒被wait的執行緒。例項如下:
執行結果如下:package com.thread.test; public class MainTestThread { public static void main(String[] aa) { final MainTestThread test = new MainTestThread(); //建立執行緒1 Thread thread = new Thread(new Runnable() { @Override public void run() { try { test.test1("執行緒1"); } catch (InterruptedException e) { e.printStackTrace(); } } }); //建立執行緒2 Thread thread1 = new Thread(new Runnable() { @Override public void run() { try { test.test1("執行緒2"); } catch (InterruptedException e) { e.printStackTrace(); } } }); //建立執行緒3 Thread thread2 = new Thread(new Runnable() { @Override public void run() { try { test.test1("執行緒3"); } catch (InterruptedException e) { e.printStackTrace(); } } }); //啟動執行緒 thread.start(); thread1.start(); thread2.start(); } public void test1(String thread) throws InterruptedException { synchronized (this) { for (int j = 1; j <= 5; j++) { //當為1的時候,表示進來新執行緒 if (j == 1) { System.out.println(thread + "進入"); } //為3的時候,暫時當前執行緒,並且釋放物件鎖 if (j == 3) { this.wait(); } System.out.println(thread + "第" + j + "執行"); //喚醒該執行緒 this.notify(); } } } }
但是也有出現這種情況:
最後一個執行緒執行了一半,這是因為notify在喚醒休眠執行緒的時候,是隨機喚醒的。當執行緒1執行到3時,wait後,另一個執行緒進入,並且喚醒執行緒1。然後又執行執行緒1(執行緒的執行是無順序的,不會因為你先呼叫就先執行某個執行緒),那麼最後一個執行緒執行到3的時候,被休眠了。沒有其他的執行緒來喚醒,一直等待下去。使用sleep來休眠當前執行執行緒:
執行結果:package com.thread.test; import java.text.SimpleDateFormat; import java.util.Date; public class MainTestThread3 { public static void main(String[] aa){ final MainTestThread3 test=new MainTestThread3(); Thread thread=new Thread(new Runnable() { @Override public void run() { try { test.test1("執行緒1"); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread thread1=new Thread(new Runnable() { @Override public void run() { try { test.test1("執行緒2"); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread thread2=new Thread(new Runnable() { @Override public void run() { try { test.test1("執行緒3"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); thread.start(); thread1.start(); thread2.start(); } public void test1(String thread) throws InterruptedException{ SimpleDateFormat sim=new SimpleDateFormat("yyyyMMddHHmmssSS"); synchronized (this) { for (int j = 1; j <= 5; j++) { if(j==1){ System.out.println(thread+"進入"); } if(j==3){ String data1=sim.format(new Date()); Thread.sleep(3000); String data2=sim.format(new Date()); System.out.println("執行花費了:"+String.valueOf((Long.parseLong(data2)-Long.parseLong(data1))+"毫秒")); } System.out.println(thread+"第"+j+"執行"); } } } }