日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

java 等待_Java并发之等待/通知机制

發(fā)布時(shí)間:2025/4/17 59 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java 等待_Java并发之等待/通知机制 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1 前言

本篇文章默認(rèn)大家對(duì)synchronized跟ReentrantLock有一定了解。

1.1 先來(lái)段代碼放松一下

下面一段簡(jiǎn)單的代碼,主要是通過(guò)3個(gè)線程對(duì)count進(jìn)行累計(jì)來(lái)進(jìn)行模擬多線程的場(chǎng)景。

/**

* zhongxianyao

*/

public class Test {

private static final int N = 3;

private int count = 0;

public void doSomething() {

// 實(shí)際業(yè)務(wù)中,這里可能是遠(yuǎn)程獲取數(shù)據(jù)之類(lèi)的耗時(shí)操作

for (int i=0; i<1000_000; i++) {

synchronized (this) {

count ++;

}

}

}

public static void main(String[] args) throws Exception {

Test test = new Test();

for (int i=0; i

Runnable runnable = () -> test.doSomething();

new Thread(runnable).start();

}

Thread.sleep(1000);

System.out.println(test.count);

}

}

在多線程編程中,一旦調(diào)用start()后,什么時(shí)候真正分配CPU時(shí)間片運(yùn)行是不確定的,運(yùn)行多久也是不確定的,所以有時(shí)候可能根據(jù)經(jīng)驗(yàn),預(yù)估一下程序的運(yùn)行時(shí)間,然后進(jìn)行sleep,最后獲取結(jié)果。但這種方式太low了,有沒(méi)有那么一種方式,當(dāng)程序獲取到結(jié)果后進(jìn)行通知呢?下面將引出今天要講的等待/通知機(jī)制。

2 Object wait()/notify()

2.1 一段入門(mén)代碼

先來(lái)一段代碼看一下wait()/notify()的基本用法

/**

* zhongxianyao

*/

public class Test {

private static final int N = 3;

private int count = 0;

private int finishCount = 0;

public void doSomething() {

for (int i=0; i<1000_000; i++) {

synchronized (this) {

count ++;

}

}

synchronized (this) {

finishCount ++;

notify();

}

}

public static void main(String[] args) {

Test test = new Test();

for (int i=0; i

Runnable runnable = () -> test.doSomething();

new Thread(runnable).start();

}

synchronized (test) {

try {

while (test.finishCount != N) {

test.wait();

}

} catch (Exception e) {

e.printStackTrace();

}

}

System.out.println(test.count);

}

}

結(jié)果輸出3000000,結(jié)果是正確,是自己想要的。

2.2 問(wèn)題三連擊

a.為什么官方說(shuō)wait() 要放在while里面?

接口描述如下

As in the one argument version, interrupts and spurious wakeups are possible, and this method should always be used in a loop:

synchronized (obj) {

while ()

obj.wait();

... // Perform action appropriate to condition

}

翻譯一下:在一個(gè)論點(diǎn)版本中,中斷跟虛假喚醒是可能,所以這個(gè)方法應(yīng)始終放在一個(gè)循環(huán)中。

加上一句自己的解釋:一般在項(xiàng)目中,一個(gè)線程不可能無(wú)緣無(wú)故等待,總是需要在某種條件下進(jìn)行等待,而且其他線程喚醒這個(gè)線程的時(shí)候,可能用的是notifyAll(),數(shù)據(jù)被其他線程消費(fèi)了,這里需要在判斷一下是否滿足特定的條件再繼續(xù)運(yùn)行。

b.為什么wait()必須在同步方法/代碼塊中調(diào)用?

解釋1:wait()本身設(shè)計(jì)的邏輯就是在釋放鎖進(jìn)行等待,如果沒(méi)有獲取鎖,談何釋放。

解釋2:通常在wait()的方法前面都會(huì)有while語(yǔ)句的判斷,在這兩條語(yǔ)句中會(huì)有時(shí)間間隔,可能會(huì)破壞程序,需要加上synchronized同步代碼塊來(lái)保證原子操作。

c.為什么wait(), notify() 和 notifyAll()是定義在Object里面而不是在Thread里面?

因?yàn)閣ait()等方法都是鎖級(jí)別操作,再者Java提供的鎖都是對(duì)象級(jí)別的而不是線程級(jí)別的,每個(gè)對(duì)象都有鎖。如果wait()方法定義在Thread類(lèi)中,線程正在等待的是哪個(gè)鎖就不明顯了。

2.3 wait(long timeout)

在上面的例子中,如果notify();那行代碼刪除,wait()改為wait(100),如下,那么程序是否可以獲取到正確的結(jié)果呢?

/**

* zhongxianyao

*/

public class Test {

private static final int N = 3;

private int count = 0;

private int finishCount = 0;

public void doSomething() {

for (int i=0; i<1000_000; i++) {

synchronized (this) {

count ++;

}

}

synchronized (this) {

finishCount ++;

//notify();

}

}

public static void main(String[] args) {

Test test = new Test();

for (int i=0; i

Runnable runnable = () -> test.doSomething();

new Thread(runnable).start();

}

synchronized (test) {

try {

while (test.finishCount != N) {

test.wait(100);

}

} catch (Exception e) {

e.printStackTrace();

}

}

System.out.println(test.count);

}

}

運(yùn)行結(jié)果是3000000,是正確的結(jié)果,看了一下文檔,發(fā)現(xiàn)這個(gè)字段跟直覺(jué)理解的不一樣,直覺(jué)告訴我,這個(gè)是最長(zhǎng)等多久,等太久了就InterruptedException,結(jié)果不是。這個(gè)方法設(shè)置的時(shí)間,是說(shuō)等待多久就喚醒自己。

3 Condition await()/signal()

3.1 用Condition進(jìn)行替換

下面的代碼,把前一個(gè)例子中的synchronized代碼塊,換成了lock()/unlock,notify()換成了condition.signal(),wait()換成了condition.await()。運(yùn)行結(jié)果也是正確的。

/**

* zhongxianyao

*/

public class Test {

private static final int N = 3;

private int count = 0;

private int finishCount = 0;

private Lock lock = new ReentrantLock();

private Condition condition = lock.newCondition();

public void doSomething() {

for (int i=0; i<1000_000; i++) {

synchronized (this) {

count ++;

}

}

lock.lock();

finishCount ++;

if (finishCount == N) {

condition.signal();

}

lock.unlock();

}

public static void main(String[] args) {

Test test = new Test();

for (int i=0; i

Runnable runnable = () -> test.doSomething();

new Thread(runnable).start();

}

test.lock.lock();

try {

test.condition.await();

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

test.lock.unlock();

}

System.out.println(test.count);

}

}

3.2 signal()方法后不建議添加邏輯

public class ConditionTest {

public static void main(String[] args) {

ReentrantLock lock = new ReentrantLock();

Condition condition = lock.newCondition();

new Thread(() -> {

try {

long time = System.currentTimeMillis();

lock.lock();

System.out.println("await start");

condition.await();

System.out.println("await end " + (System.currentTimeMillis() - time) + "ms");

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

lock.unlock();

}

}, "Thread-await").start();

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

new Thread(() -> {

try {

lock.lock();

System.out.println("signal start");

TimeUnit.SECONDS.sleep(5);

condition.signal();

System.out.println("signal end");

} catch (Exception e) {

e.printStackTrace();

} finally {

lock.unlock();

System.out.println("signal unlock");

}

}, "Thread-signal").start();

}

}

多次運(yùn)行,結(jié)果都是一樣的,如下:

await start

signal start

signal end

signal unlock

await end 5005ms

從運(yùn)行結(jié)果可以看出,await()后,鎖就釋放了,但signal()后,鎖不釋放,一定要在unlock()之后,鎖才釋放,await()才會(huì)往下執(zhí)行。

既然喚醒了其他線程,又不釋放鎖,可以調(diào)整喚醒的時(shí)機(jī)。一般在實(shí)際代碼中,也是不建議signal()方法后添加邏輯,應(yīng)該直接釋放鎖。

同理,上面的notify()也是在synchronized代碼塊結(jié)束后,wait()后面的語(yǔ)句才能真正執(zhí)行。

3.3 boolean await(long time, TimeUnit unit)

把上面的condition.await()改為condition.await(1, TimeUnit.SECONDS),然后獲取返回值,運(yùn)行結(jié)果返回的是false。

這個(gè)時(shí)候,如果把TimeUnit.SECONDS.sleep(5),condition.signal()這兩行代碼順序調(diào)換一下,那么await的返回值就是true。

再看到官方文檔對(duì)這個(gè)返回值的描述,如下

{@code false} if the waiting time detectably elapsed

before return from the method, else {@code true}

翻譯過(guò)來(lái),大致意思就是“如果等待時(shí)間可以在方法返回之前檢測(cè)到返回false,否則返回true”。但實(shí)際測(cè)試結(jié)果卻是await()被喚醒的時(shí)候,而不是方法返回的時(shí)候。

4 區(qū)別

Object wait() notify() 搭配synchronized使用

Condition await() signal() 搭配Lock使用

Object notify() 是隨機(jī)喚醒一個(gè)

Condition signal() 是喚醒第一個(gè)await()的線程

Object wait()有虛假喚醒,而Condition await() 沒(méi)有

5 參考文檔

總結(jié)

以上是生活随笔為你收集整理的java 等待_Java并发之等待/通知机制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。