講解等待喚醒機制之前,有必要搞清一個概念——
線程之間的通信:
多個線程在處理同一個資源,但是處理的動作(線程的任務)卻不相同。通過一定的手段使各個線程能有效的利用資源。而這種手段即—— 等待喚醒機制。
等待喚醒機制所涉及到的方法:
wait() :等待,將正在執行的線程釋放其執行資格 和 執行權,並存儲到線程池中。
notify():喚醒,喚醒線程池中被wait()的線程,一次喚醒一個,而且是任意的。
notifyAll(): 喚醒全部:可以將線程池中的所有wait() 線程都喚醒。
public class NumberHolder { private int number; public synchronized void increase() { if (0 != number) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 能執行到這裏說明已經被喚醒 // 並且number爲0 number++; System.out.println(number); // 通知在等待的線程 notify(); } public synchronized void decrease() { if (0 == number) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 能執行到這裏說明已經被喚醒 // 並且number不爲0 number--; System.out.println(number); notify(); } }public class IncreaseThread extends Thread { private NumberHolder numberHolder; public IncreaseThread(NumberHolder numberHolder) { this.numberHolder = numberHolder; } @Override public void run() { for (int i = 0; i < 20; ++i) { // 進行一定的延時 try { Thread.sleep((long) Math.random() * 1000); } catch (InterruptedException e) { e.printStackTrace(); } // 進行增加操作 numberHolder.increase(); } } }public class DecreaseThread extends Thread { private NumberHolder numberHolder; public DecreaseThread(NumberHolder numberHolder) { this.numberHolder = numberHolder; } @Override public void run() { for (int i = 0; i < 20; ++i) { // 進行一定的延時 try { Thread.sleep((long) Math.random() * 1000); } catch (InterruptedException e) { e.printStackTrace(); } // 進行減少操作 numberHolder.decrease(); } } }public class NumberTest { public static void main(String[] args) { NumberHolder numberHolder = new NumberHolder(); Thread t1 = new IncreaseThread(numberHolder); Thread t2 = new DecreaseThread(numberHolder); t1.start(); t2.start(); } }
因爲就是兩個線程所以就是可以互相切換,即必須是互相切換,但是如果是四個線程呢?會怎麼樣呢?
那麼再來兩個線程;
即把其中的NumberTest類改爲如下:
public class NumberTest { public static void main(String[] args) { NumberHolder numberHolder = new NumberHolder(); Thread t1 = new IncreaseThread(numberHolder); Thread t2 = new DecreaseThread(numberHolder); Thread t3 = new IncreaseThread(numberHolder); Thread t4 = new DecreaseThread(numberHolder); t1.start(); t2.start(); t3.start(); t4.start(); } }
爲什麼兩個線程的時候執行結果正確而四個線程的時候就不對了呢?
因爲線程在wait()的時候,接收到其他線程的通知,即往下執行,不再進行判斷。兩個線程的情況下,喚醒的肯定是另一個線程;但是在多個線程的情況下,執行結果就會混亂無序。
比如,一個可能的情況是,一個增加線程執行的時候,其他三個線程都在wait,這時候第一個線程調用了notify()方法,其他線程都將被喚醒,然後執行各自的增加或減少方法。
解決的方法就是:在被喚醒之後仍然進行條件判斷,去檢查要改的數字是否滿足條件,如果不滿足條件就繼續睡眠。把兩個方法中的if改爲while即可。
public class NumberHolder { private int number; public synchronized void increase() { while (0 != number) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 能執行到這裏說明已經被喚醒 // 並且number爲0 number++; System.out.println(number); // 通知在等待的線程 notify(); } public synchronized void decrease() { while (0 == number) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 能執行到這裏說明已經被喚醒 // 並且number不爲0 number--; System.out.println(number); notify(); } }
這樣就可以解決了線程的混亂的問題。