java线程虚假唤醒_实战之java中线程的虚假唤醒

本文详细介绍了Java中因虚假唤醒导致的问题及解决方案,包括使用while循环而非if条件判断、设置超时时间等方法,确保线程正确等待条件满足。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

出现虚假唤醒的地方java.lang.Object#wait()方法和它的重载方法

java.util.concurrent.locks.Condition#await()方法和它的重载方法

java.util.concurrent.locks.Condition#awaitUntil方法与它的重载方法

解决办法

1. 在java.lang.Object#wait()方法上面有这样一段注释:

Causesthe current thread to waituntilanother thread invokes the

*{@linkjava.lang.Object#notify()} method or the

*{@linkjava.lang.Object#notifyAll()} method for this object.

*Inother words,thismethod behaves exactlyasifit simply

*performs the call{@codewait(0)}.

*

*Thecurrent thread must ownthisobject's monitor. The thread

* releases ownership of this monitor and waits until another thread

* notifies threads waiting on this object's monitor to wake up

*either through a call to the{@codenotify}methodorthe

*{@codenotifyAll}method.Thethreadthenwaitsuntilit can

*re-obtain ownership of the monitorandresumes execution.

*

Asinthe one argument version,interruptsandspurious wakeups are

*possible,andthismethod should always be usedina loop:

*

*synchronized(obj){

*while(<condition doesnothold>)

*obj.wait();

*...// Perform action appropriate to condition

*}

*

*Thismethod should only be calledbya thread thatisthe owner

*ofthisobject's monitor. See the {@code notify} method for a

* description of the ways in which a thread can become the owner of

* a monitor.

可以看到,这里提供的方法是使用while循环,重新检测条件或者timeout。

2. 其他解法

1.等待直到条件变成true

不正确的代码示范:

Thread 1:synchronized(this){

if(!condition){

wait();

}

doStuffAssumingConditionIsTrue();

}

Thread 2:

synchronized(this){

condition=true;

notify();

}

如果wait()方法被虚假唤醒,然后doStuffAssumingConditionIsTrue()会被执行,尽管此时condition的值是false。如果用while来代替while

Thread 1:synchronized(this){

while(!condition){

wait();

}

doStuffAssumingConditionIsTrue();

}

这就保证了只有在condition为true时doStuffAssumingConditionIsTrue()才会被执行。需要注意的一点是,condition变量必须在同步代码块内部;否则的话,你将会在对condition变量判断和设值时存在一个竞态条件。

2. 等待直到一个事件发生

不正确的代码示范:

Thread 1:

synchronized(this){

wait();

doStuffAfterEvent();

}

Thread 2:// when event occurs

synchronized(this){

notify();

}

如果wait()方法被虚假唤醒,doStuffAfterEvent() 会在事件还没有发生时就执行。可以参考上面例子中的while循环方式来重写这个方法。

3. 等待直到条件变成true或者超时时

不正确的代码示范:

synchronized(this){

if(!condition){

wait(timeout);

}

doStuffAssumingConditionIsTrueOrTimeoutHasOccurred();

}

虚假唤醒会导致doStuffAssumingConditionIsTrueOrTimeoutHasOccurred()方法在条件仍然为false且还没有超时时被执行。

应该这样写:synchronized(this){

longnow=System.currentTimeMillis();

longdeadline=now+timeout;

while(!condition&&now

wait(deadline-now);

now=System.currentTimeMillis();

}

doStuffAssumingConditionIsTrueOrTimeoutHasOccurred();

}

4.等待固定长的时间

第一种,警告:这种类型的waiting/sleeping常常用于本意就是等待所有的操作完成,然后执行的场景。如果你是这种场景,最好考虑用上面示例中的方式重写你的代码。否则你必须依赖系统时间,系统时间在不同机器上是不一样的。

错误的代码示范:

synchronized(this){

// Give some time for the foos to bar

wait(1000);

}

虚假唤醒不会等待完整的1000 ms. Thread.sleep(),不会被虚假唤醒,所以你应该使用Thread.sleep()来代替。Thread.sleep(1000);

5. 一直等待

错误代码示范:

synchronized(this){

// wait forever

wait();

}

虚假唤醒会导致它不会永久等待,需要把wait() 包裹在 while (true) 循环中:synchronized(this){

// wait forever

while(true){

wait();

}

}

6. 比较乐观的例子(认为不会虚假唤醒)

WaitNotInLoopPositiveCases.java:

/*

* Copyright 2013 The Error Prone Authors.

*

* Licensed under the Apache License, Version 2.0 (the 'License');

* you may not use this file except in compliance with the License.

* You may obtain a copy of the License at

*

* http://www.apache.org/licenses/LICENSE-2.0

*

* Unless required by applicable law or agreed to in writing, software

* distributed under the License is distributed on an 'AS IS' BASIS,

* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

* See the License for the specific language governing permissions and

* limitations under the License.

*/

packagecom.google.errorprone.bugpatterns.testdata;

importjava.util.Date;

importjava.util.concurrent.TimeUnit;

importjava.util.concurrent.locks.Condition;

/** @author eaftan@google.com (Eddie Aftandilian) */

publicclassWaitNotInLoopPositiveCases{

booleanflag=false;

publicvoidtestIfInsteadOfLoop(){

synchronized(this){

if(!flag){

try{

// BUG: Diagnostic contains: wait() must always be called in a loop

// Did you mean 'while (!flag) {'?

wait();

}catch(InterruptedExceptione){

}

}

}

}

publicvoidtestWaitLong()throwsInterruptedException{

// BUG: Diagnostic contains: wait(long) must always be called in a loop

wait(1000);

}

publicvoidtestWaitLongInt()throwsException{

// BUG: Diagnostic contains: wait(long,int) must always be called in a loop

wait(1000,1000);

}

publicvoidtestAwait(Conditioncond)throwsException{

// BUG: Diagnostic contains: await() must always be called in a loop

cond.await();

}

publicvoidtestAwaitWithFix(Conditioncond)throwsException{

synchronized(this){

if(!flag){

try{

// BUG: Diagnostic contains: await() must always be called in a loop

// Did you mean 'while (!flag) {'?

cond.await();

}catch(InterruptedExceptione){

}

}

}

}

publicvoidtestAwaitLongTimeUnit(Conditioncond)throwsException{

// BUG: Diagnostic contains:

// await(long,java.util.concurrent.TimeUnit) must always be called in a loop

cond.await(1,TimeUnit.SECONDS);

}

publicvoidtestAwaitNanos(Conditioncond)throwsException{

// BUG: Diagnostic contains: awaitNanos(long) must always be called in a loop

cond.awaitNanos(1000000);

}

publicvoidtestAwaitUninterruptibly(Conditioncond)throwsException{

// BUG: Diagnostic contains: awaitUninterruptibly() must always be called in a loop

cond.awaitUninterruptibly();

}

publicvoidtestAwaitUntil(Conditioncond)throwsException{

// BUG: Diagnostic contains: awaitUntil(java.util.Date) must always be called in a loop

cond.awaitUntil(newDate());

}

}

7. 悲观的例子(认为会虚假唤醒)

WaitNotInLoopNegativeCases.java:/*

* Copyright 2013 The Error Prone Authors.

*

* Licensed under the Apache License, Version 2.0 (the 'License');

* you may not use this file except in compliance with the License.

* You may obtain a copy of the License at

*

* http://www.apache.org/licenses/LICENSE-2.0

*

* Unless required by applicable law or agreed to in writing, software

* distributed under the License is distributed on an 'AS IS' BASIS,

* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

* See the License for the specific language governing permissions and

* limitations under the License.

*/

packagecom.google.errorprone.bugpatterns.testdata;

/**

* @author eaftan@google.com (Eddie Aftandilian)

*

TODO(eaftan): Add test cases for enhanced for loop, loop outside synchronized block.

*/

publicclassWaitNotInLoopNegativeCases{

booleanflag=true;

publicvoidtest1(){

synchronized(this){

while(!flag){

try{

wait();

}catch(InterruptedExceptione){

}

}

}

}

publicvoidtest2(){

synchronized(this){

while(!flag){

try{

wait(1000);

}catch(InterruptedExceptione){

}

}

}

}

publicvoidtest3(){

synchronized(this){

while(!flag){

try{

wait(1000,1000);

}catch(InterruptedExceptione){

}

}

}

}

// This code is incorrect, but this check should not flag it.

publicvoidtestLoopNotInSynchronized(){

while(!flag){

synchronized(this){

try{

wait();

}catch(InterruptedExceptione){

}

}

}

}

publicvoidtestDoLoop(){

synchronized(this){

do{

try{

wait();

}catch(InterruptedExceptione){

}

}while(!flag);

}

}

publicvoidtestForLoop(){

synchronized(this){

for(;!flag;){

try{

wait();

}catch(InterruptedExceptione){

}

}

}

}

publicvoidtestEnhancedForLoop(){

int[]arr=newint[100];

synchronized(this){

for(inti:arr){

try{

wait();

}catch(InterruptedExceptione){

}

}

}

}

privatevoidwait(Objectobj){}

publicvoidtestNotObjectWait(){

wait(newObject());

}

}

上面的内容译自:http://errorprone.info/bugpattern/WaitNotInLoop

3. 参考:

http://errorprone.info/bugpattern/WaitNotInLoop

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值