Java是目前最流行的編程語言之一,其並發編程能力也是相當強大的。在多線程編程中,線程間的通信無疑是非常關鍵的。Java提供了多種方式來實現線程間通信,其中最常見的方式之一就是使用對象的wait()和notify()方法。這種方式不僅簡單易用,而且在Java中廣泛應用於各種場景下。
一、wait-notify機制的概述
wait-notify機制是Java中實現線程間通信的一種機制。它通過在不同的線程之間共享對象來實現線程間的通信。具體來說,某個線程調用對象的wait()方法後,會讓該線程處於等待狀態,並且該線程釋放所持有的對象鎖;而另一個線程調用該對象的notify()方法後,會使得等待中的線程被喚醒並且重新競爭對象鎖。因此,wait-notify機制可以幫助我們更好地控制線程的執行。
二、wait-notify機制的使用
1. wait()方法
java.lang.Object提供了wait()方法,用於將當前的線程置入「睡眠」狀態,且只有等待另一個線程來喚醒此線程。wait()方法常用於線程間交互/通信的場合,通常有線程A調用了對象的wait()方法進入等待狀態,而線程B調用了相同對象的notify()或notifyAll()方法,使得該對象的等待線程重新進入可運行狀態。
/**
* wait方法
*/
public class WaitDemo implements Runnable{
private static Object object = new Object();
@Override
public void run() {
synchronized (object){
try {
System.out.println(Thread.currentThread().getName() + " wait開始..");
object.wait();
System.out.println(Thread.currentThread().getName() + " 被喚醒..");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
WaitDemo waitDemo1 = new WaitDemo();
WaitDemo waitDemo2 = new WaitDemo();
Thread thread1 = new Thread(waitDemo1, "線程1");
Thread thread2 = new Thread(waitDemo2, "線程2");
thread1.start();
thread2.start();
System.out.println("主線程睡眠2秒開始..");
Thread.sleep(2000); //主線程睡眠3秒
System.out.println("主線程睡眠2秒結束..");
synchronized (object){
object.notifyAll();
}
System.out.println("主線程喚醒等待線程結束..");
}
}
注意事項:
- wait方法必須使用在synchronized同步中。
- wait方法會釋放鎖,而notity方法不會釋放鎖。
- wait方法和notify,notifyAll方法不會自動釋放鎖。
2. notify()方法
java.lang.Object提供了notify()方法,喚醒一個因調用了wait()方法而處於阻塞狀態的線程。
/**
* notify方法
*/
public class NotifyDemo implements Runnable{
private static Object object = new Object();
@Override
public void run() {
synchronized (object){
System.out.println(Thread.currentThread().getName() + " 進入等待狀態..");
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 被喚醒..");
}
}
public static void main(String[] args) {
NotifyDemo notifyDemo = new NotifyDemo();
Thread thread1 = new Thread(notifyDemo, "線程1");
Thread thread2 = new Thread(notifyDemo, "線程2");
thread1.start();
thread2.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (object){
object.notify(); //隨機喚醒一個線程
//object.notifyAll(); 喚醒所有線程
}
System.out.println("主線程喚醒所有線程結束..");
}
}
三、wait-notify機制的注意事項
1. Wait和Notify的阻塞隊列
線程在爭取到鎖之後,如果發現自己無法繼續執行,就會釋放鎖,並進入該鎖對象的等待隊列(wait-set)中。在鎖被釋放的同時,該線程也進入到該鎖的等待隊列中,並且會掛起線程。當鎖被另一個線程重新競爭並成功獲取後,它就會喚醒該鎖等待隊列中的某個線程,喚醒的操作由notify或者notifyAll方法完成。
2. 避免因過多的notify()而導致の虛假喚醒(Spurious Wakeup)問題
由於操作系統底層的原因,某些情況下JVM會在沒有notify()調用的情況下自動喚醒waiting線程。例如:CPU資源不足,只要有線程有等待狀態結束了,它就會進行喚醒操作。
為了避免這種情況的發生,往往需要將在wait()的時候用while來判斷條件,以保證線程在喚醒後,會重新判斷條件並決定是否執行後續的工作。以下是一個示例代碼:
/**
* 避免虛假喚醒問題
*/
public class WaitNotifyDemo implements Runnable{
private static final Object object = new Object();
private static boolean flag;
@Override
public void run() {
synchronized (object){
while (!flag){
try {
System.out.println(Thread.currentThread().getName() + "等待中..");
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "被喚醒..");
}
}
public static void main(String[] args) {
WaitNotifyDemo waitNotifyDemo = new WaitNotifyDemo();
Thread thread1 = new Thread(waitNotifyDemo, "線程1");
Thread thread2 = new Thread(waitNotifyDemo, "線程2");
thread1.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (object){
flag = true;
System.out.println("flag = true,喚醒所有線程..");
object.notifyAll();
}
}
}
總結
本文介紹了Java中實現線程間通信的利器——wait-notify機制。wait-notify機制通過在不同的線程之間共享對象來實現線程間的通信,使用起來簡單易懂,適用於各種場景。然而在使用過程中,需注意wait-notify機制的細節和注意事項。我們通過編寫示例代碼,希望能幫助讀者更好地理解wait-notify機制。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/295990.html