「这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战」
虚假唤醒
在jdk官方文档中查看wait()方法,如果判断等待的条件判断方法错误,就会存在虚假等待
现在有一个场景:线程A和线程B交替执行,A、B操作同一个变量 num = 0,线程A进行+1操作,线程B执行-1操作
当只有两个线程去进行操作的时候,是没有问题的
但是当线程数增加到4个两个线程去进行增加操作,两个线程去进行减少操作就会出现问题
运行结果中出现,2,3数字证明并发并没有控制住,原因是因为在判断等待条件时,代码中是采用的if判断,两个线程进来同时进行操作只判断了一次,这就是虚假唤醒
if 改为 while
安装官方文档中去解决这个问题,改为 while 判断就可以,将代码中的条件改为while,查看运行结果成功解决了这个问题
Lock 实现生产者消费者问题
在之前的synchronized版本juejin.cn/post/703006… 里面使用wait()和 notify() 实现等待和通知
查看Lock的接口文档可以找到Condition,发现里面并不是使用 wait和notify 而是 await() 和 signal()
将上面的场景改为用Lock实现
public class TestPc4 {
public static void main(String[] args) {
Data2 data = new Data2();
new Thread(() -> {
for (int i = 0; i < 3; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 3; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 3; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "C").start();
new Thread(() -> {
for (int i = 0; i < 3; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "D").start();
}
}
class Data2 {
private Integer number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void increment() throws InterruptedException {
lock.lock();
try {
while (number != 0) {
//等待
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName() + "-->" + number);
//通知其他线程+1完成
condition.signal();
}catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException {
lock.lock();
try{
while (number == 0) {
condition.await();
}
number--;
//通知其他线程-1完成
System.out.println(Thread.currentThread().getName() + "-->" + number);
condition.signal();
}catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
复制代码
查看运行结果加锁成功
但是从结果上来线程的分配结果是随机的看这个和之前用synchronized实现没有什么什么差别,可是Condition是在synchronized之后的方法,应该是有它的一些优势的。Condition是可以实现A线程执行完了去通知B执行,B执行完了去通知C执行
Condition 实现精准通知唤醒
使用Condition控制线程执行,A执行完了执行B,B执行完了执行C
实现demo
public class TestCondition {
public static void main(String[] args) {
Data3 data3 = new Data3();
new Thread(() -> {
for (int i = 0; i < 2; i++) {
data3.printA();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 2; i++) {
data3.printB();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 2; i++) {
data3.printC();
}
}, "C").start();
}
}
//资源类
class Data3 {
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
private Integer number = 1;
public void printA() {
lock.lock();
try {
//业务 判断 执行 通知
while (number != 1) {
condition1.await();
}
System.out.println(Thread.currentThread().getName() + "-->AAAA");
//唤醒 唤醒指定人B
number = 2;
condition2.signal();
}catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void printB() {
lock.lock();
try {
//业务 判断 执行 通知
while (number != 2) {
condition2.await();
}
System.out.println(Thread.currentThread().getName() + "-->BBBB");
//唤醒
number = 3;
condition3.signal();
}catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void printC() {
lock.lock();
try {
//业务 判断 执行 通知
while (number != 3) {
condition3.await();
}
System.out.println(Thread.currentThread().getName() + "-->CCCC");
//唤醒
number = 1;
condition1.signal();
}catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
复制代码
运行结果
通过设置不同的监视器进行通知,可以看到A、B、C是依次执行的
近期评论