一、介紹
此任務的目標是安全、高效地使一個運行着的線程開始進入wait狀態以及從wait狀態中喚醒。而此操作都是通過其他線程被動地使當前線程將運行狀態的強制轉換,具體實現方式不妨就從代碼出發吧!
二、代碼示例
import java.util.concurrent.atomic.AtomicInteger;
/**
* 實現線程的被動wait,依靠其餘線程發出信號進行線程的wait\notify,而不是本身根據線程數目的限定而控制等待和wait.
*
* 以下toWait方法爲將當前線程置爲wait狀態
* toNotify 方法爲將當前線程置爲喚醒狀態
*
* 其中有一個大區別就是一個方法需要同步監視代碼塊,而另一個不需要
*/
public class PessimisticWait {
static final Object MONITOR = new Object();
static AtomicInteger i = new AtomicInteger();
public static void main(String[] args) {
ThreadTest threadTest = new ThreadTest();
threadTest.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadTest.toWait();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadTest.toNotify();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadTest.toWait();
}
static class ThreadTest extends Thread {
private boolean flag = false;
@Override
public void run() {
System.out.println("線程進入第一次運行狀態");
while (true) {
synchronized (MONITOR) {
if (flag) {
try {
System.out.println("線程開始wait");
MONITOR.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
i.getAndIncrement();
System.out.println("當前線程正再執行" + i);
}
}
public void toWait() {
flag = true;
}
public void toNotify(){
synchronized (MONITOR){
flag =false;
MONITOR.notify();
System.out.println("線程wait結束,喚醒線程");
}
}
}
}
控制檯輸出:
線程進入第一次運行狀態
當前線程正再執行1
當前線程正再執行2
當前線程正再執行3
當前線程正再執行4
當前線程正再執行5
當前線程正再執行6
當前線程正再執行7
當前線程正再執行8
當前線程正再執行9
當前線程正再執行10
線程開始wait
線程wait結束,喚醒線程
當前線程正再執行11
當前線程正再執行12
當前線程正再執行13
當前線程正再執行14
當前線程正再執行15
當前線程正再執行16
當前線程正再執行17
當前線程正再執行18
當前線程正再執行19
當前線程正再執行20
線程開始wait
三、代碼分析
代碼實現的功能:上述代碼實現了在main
線程中將ThreadTest
線程對象通過waitf
方法強制進入阻塞狀態以及通過notify
方法喚醒。
注意事項:toWait()
和toNotify()
方法實現了當前線程的進入wait狀態以及喚醒操作。但是我問讀者朋友們一個問題:爲何前者沒有使用synchronized
,而後者卻需要呢?
首先,我們wait
方法一般通過監視器對象來進行調用,如果在toWait()
中使用synchronized
關鍵字進行監視器同步代碼塊處理,那麼在main
方法中調用時,當前線程爲main
線程,這樣一來只會錯將main
線程阻塞。所以我們設計一個flag來控制是否使threadTest
對象自己來調用wait
方法,這就保證了阻塞線程對象的正確性。
其次,當前由於釋放MONITOR
對象而阻塞的線程只有一個,即threadTest
對象,所以我們只需在同步監視器內調用 MONITOR.notify();
方法即能喚醒threadTest
線程,並且此方法不與當前運行的線程有關,所以需要在toNotify
方法內部上鎖時即使當前線程是main
線程,也是沒有影響的。