你的代码线程安全么?(二)

本文探讨了在Java中如何正确地使用wait方法来实现线程间的同步,并解释了为什么wait应该总是放在while循环中而不是if条件判断内,以防出现假唤醒等问题。

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

当多个线程由于不同原因而在监视器的等待队列中等待时,需要将wait保护在while循环中:

while (conditionIsNotTrue)
    wait()

这样可以确保线程被唤醒的确是由于所等待的条件变为真了。但是如果条件队列中的所有线程都在等待同一个条件,那么是否可以将wait包含在if判断中呢?

if (conditionIsNotTrue)
    wait()

不能,原因有三:

1.子类极易破坏原有的代码同步策略。子类有可能在同一监控器上为不同的条件而等待,从而父类的等待队列中线程所等待的条件就不再唯一了,例如下面的例子中,调用Room.comeIn()方法的线程原本只会因为isSeatsAreAvailable条件不能满足而等待,因此使用if可以认为是安全的。但是正如OfficeRoom所作的,调用OfficeRoom.bossComeToOffice()的线程会因为isOfficeEmpty不能满足而阻塞,同样线程也会因为isSeatsAreAvailable而阻塞。这是只用if判断就是不安全的了。

2. 如果将Romm声明为final类型呢?仍然是不安全的。尽管线程阻塞的原因一定是唯一的了,但是唤醒线程的原因并不唯一。 除了执行Room.releaseSeat()的线程会唤醒等待isSeatsAreAvailable的线程外,操作系统还会产生“假唤醒”,在毫无理由的情况下,把等待isSeatsAreAvailable的线程唤醒。对于这种行为,JVM规范并未做任何限制。因此,永远不能假设线程从wait()中被唤醒后会处于何种状态,wait()一定要在while循环中被保护起来。

----------
class Room {

    public synchronized void comeIn() {
        if (!isSeatsAreAvailable()) {
            wait();
        }
        doComeIn();
    }

    public synchronized void releaseSeat() {
        notifyAll();
     }
   
}

class OfficeRoom extends Room {

    public synchronized void bossComeToOffice() {
        if (!isOfficeEmpty()) {
            wait();
        }
    }

   
    public synchronized void emptyOffice() {
        notifyAll();
    }
}
----------

3. 代码不健壮。由于wait,notify,notifyAll都是公共方法,所以客户端代码很容易(或故意)调用notify/notifyAll,从而导致代码失败。

综上,wait()方法永远要被放在while循环中。或者说,对于一个从wait等待中被唤醒的线程,在继续执行前,一定要再次检查它的先验条件是否已经得到满足。否则,你的代码就不是线程安全的了!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值