文章目錄
-
-
- API介紹
- 原理
- 使用:
-
- 正确的使用姿勢
- join原理
- Park & Unpark
-
- 線程狀态
-
API介紹
-
讓進入 object 螢幕的線程到 waitSet 等待obj.wait()
-
在 object 上正在 waitSet 等待的線程中挑一個喚醒obj.notify()
-
讓 object 上正在 waitSet 等待的線程全部喚醒obj.notifyAll()
原理
- Owner 線程發現條件不滿足,調用 wait 方法,即可進入 WaitSet 變為 WAITING 狀态
- BLOCKED 和 WAITING 的線程都處于阻塞狀态,不占用 CPU 時間片
- BLOCKED 線程會在 Owner 線程釋放鎖時喚醒
- WAITING 線程會在 Owner 線程調用 notify 或 notifyAll 時喚醒,但喚醒後并不意味者立刻獲得鎖,仍需進入 EntryList 重新競争
使用:
它們都是線程之間進行協作的手段,都屬于 Object 對象的方法。必須獲得此對象的鎖,才能調用這幾個方法
final static Object obj = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (obj) {
log.debug("執行....");
try {
obj.wait(); // 讓線程在obj上一直等待下去
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("其它代碼....");
}
}).start();
new Thread(() -> {
synchronized (obj) {
log.debug("執行....");
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("其它代碼....");
}
}).start();
sleep(2);
log.debug("喚醒 obj 上其它線程");
synchronized (obj) {
obj.notify();
}
補充:
wait(long n)
有時限的等待, 到 n 毫秒後結束等待,或是被 notify
sleep(long n)
和
wait(long n)
的差別
- sleep 是 Thread 方法,而 wait 是 Object 的方法
- sleep 不需要強制和 synchronized 配合使用,但 wait 需要 和 synchronized 一起用
- sleep 在睡眠的同時,不會釋放對象鎖的,但 wait 在等待的時候會釋放對象鎖
- 它們 狀态 TIMED_WAITING
正确的使用姿勢
由于虛假喚醒的問題,可以使用一個while來優化
模闆:
synchronized(lock) {
while(條件不成立) {
lock.wait();
}
// 幹活
}
//另一個線程
synchronized(lock) {
lock.notifyAll();
}
join原理
public final void join() throws InterruptedException {
join(0);
}
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
//線程存活,就一直等
while (isAlive()) {
wait(0);
}
} else {
//有時限的等待
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
Park & Unpark
必須注意的是,隻能先park在unpark
底層不是java可見的
使用方法
Thread t1 = new Thread(() -> {
log.debug("start...");
sleep(1);
log.debug("park...");
LockSupport.park();
log.debug("resume...");
}, "t1"); t1.start();
sleep(2);
log.debug("unpark...");
LockSupport.unpark(t1);
原理:
使用park
- 目前線程調用 Unsafe.park() 方法
- 檢查 _counter ,本情況為 0,這時,獲得 _mutex 互斥鎖
- 線程進入 _cond 條件變量阻塞
- 設定 _counter = 0
使用unpark
- 調用 Unsafe.unpark(Thread_0) 方法,設定 _counter 為 1
- 喚醒 _cond 條件變量中的 Thread_0
- Thread_0 恢複運作
- 設定 _counter 為 0
從原理中看出為什麼不能先用unpark,因為會使标志位變為1,下一次park就停不住
線程狀态
-
NEW --> RUNNABLE
當調用 t.start() 方法時,由 NEW --> RUNNABLE
-
RUNNABLE <–> WAITING
t 線程用 synchronized(obj) 擷取了對象鎖後
- 調用 obj.wait() 方法時,t 線程從 RUNNABLE --> WAITING
- 調用 obj.notify() , obj.notifyAll() , t.interrupt() 時 ,隻是讓他們恢複競争而已
- 競争鎖成功,t 線程從 WAITING --> RUNNABLE
- 競争鎖失敗,t 線程從 WAITING --> BLOCKED
- RUNNABLE <–> WAITING
- 目前線程調用 t.join() 方法時,目前線程從 RUNNABLE --> WAITING
- 注意是目前線程在t 線程對象的螢幕上等待
- t 線程運作結束,或調用了目前線程的 interrupt() 時,目前線程從 WAITING --> RUNNABLE
- 目前線程調用 t.join() 方法時,目前線程從 RUNNABLE --> WAITING