测试代码:
/**
* 现在两个线程,可以操作初始值为零的一个变量,
* 实现一个线程对该变量加1,一个线程对该变量减1,交替,来10轮。
*/
public class ThreadTest {
public static void main(String[] args) {
Number num = new Number();
//启动四个线程,循环十次
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
num.plus();
} catch (Exception e) {
e.printStackTrace();
}
},"a").start();
new Thread(() -> {
try {
num.reduce();
} catch (Exception e) {
e.printStackTrace();
}
},"b").start();
new Thread(() -> {
try {
num.plus();
} catch (Exception e) {
e.printStackTrace();
}
},"c").start();
new Thread(() -> {
try {
num.reduce();
} catch (Exception e) {
e.printStackTrace();
}
},"d").start();
}
}
}
//共享资源
class Number {
int init = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void plus() {
//加锁
lock.lock();
try {
//这里会出现虚假唤醒的问题
if (init != 0) {
//线程挂起
condition.await();
}
++ init;
System.out.println(Thread.currentThread().getName() + ":" + this.init);
//唤醒线程
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void reduce() {
lock.lock();
try {
if (init == 0) {
condition.await();
}
-- init;
System.out.println(Thread.currentThread().getName() + ":" + this.init);
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
执行结果:
a:1
b:0
c:1
d:0
a:1
b:0
c:1
d:0
c:1
d:0
b:-1
b:-2
d:-3
c:-2
a:-1
a:0
结果分析:
执行后发现线程没有按照预想交替打印,参考jdk Api
A thread can also wake up without being notified, interrupted, or timing out, a so-called spurious wakeup. While this will rarely occur in practice, applications must guard against it by testing for the condition that should have caused the thread to be awakened, and continuing to wait if the condition is not satisfied. In other words, waits should always occur in loops, like this one:
synchronized (obj) {
while (<condition does not hold>)
obj.wait(timeout);
... // Perform action appropriate to condition
}
发现wait( ) 方法会存在虚假唤醒, 这里给出了解决方案把wait() 放入循环中 就可以避免虚假唤醒的问题。
public void plus() {
//加锁
lock.lock();
try {
//这里会出现虚假唤醒的问题
while (init != 0) {
//线程挂起
condition.await();
}
++ init;
System.out.println(Thread.currentThread().getName() + ":" + this.init);
//唤醒线程
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
原理分析: