juc高级特性——虚假唤醒

juc高级特性——虚假唤醒

今天看JDK文档中的Object.wait()方法,有一段提到:

对于某一个参数的版本,实现中断和虚假唤醒是可能的,而且此方法应始终在循环中使用:

synchronized (obj) {

while (<condition>)/<condition>

obj.wait();

... // Perform action appropriate to condition

}

大致意思就是,wait()方法需要在循环中使用,以避免实现虚假唤醒。那什么是虚假唤醒,话不多说,贴一段代码来看:

Thread-1:

while(true) {

obj = queue.get(); //2

// 3

}

Thread-2:

synchronized(lock) {

// 代码一

while(queue.isEmpty()) {

lock.wait();

// 4

}

obj = queue.get();// 5

// 代码二(可能导致虚假唤醒)

lock.wait();

// 4

obj = queue.get();// 5

}

Thread-3:

synchronized(lock) {

queue.add(obj);

lock.notify();// 1

}

从上面的伪代码看到:

一、有三个线程,对同一数据进行访问

二、线程1未对数据进行安全访问

三、初始状态为:queue.size()==0,代码执行顺序为: 1-->2-->3-->4-->5

四、线程2的目的是当queue为空时等待,不为空时取出数据进行处理

五、线程2的代码一、代码二只能使用其中一种方式。

结论:当线程2使用代码一时没有问题,当使用代码二时,此时线程被唤醒,但仍然取不到数据,这就是虚假唤醒。

虚假唤醒发生的条件为:

1、当一个数据存在三个及以上的线程竞争访问时(必要条件)

2、至少有一个线程没有对数据进行加锁访问(充分条件,使得虚假唤醒发生可能)

当满足两个条件才可能发生虚假唤醒。仅仅是可能,如果代码执行顺序为:1-->4-->5-->2-->3,线程二可以取到数据,也就不存在虚假唤醒。

解决虚假唤醒(以下任意一种方式即可):

1、所有的线程访问数据时都加锁(线程一就没有加锁)

2、在循环中等待(线程2中的代码一)


分享到:


相關文章: