【多线程学习】Lock锁版的生产者消费者问题

「这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战

虚假唤醒

在jdk官方文档中查看wait()方法,如果判断等待的条件判断方法错误,就会存在虚假等待

image.png

现在有一个场景:线程A和线程B交替执行,A、B操作同一个变量 num = 0,线程A进行+1操作,线程B执行-1操作

当只有两个线程去进行操作的时候,是没有问题的

image.png

但是当线程数增加到4个两个线程去进行增加操作,两个线程去进行减少操作就会出现问题

image.png

运行结果中出现,2,3数字证明并发并没有控制住,原因是因为在判断等待条件时,代码中是采用的if判断,两个线程进来同时进行操作只判断了一次,这就是虚假唤醒

image.png

if 改为 while

安装官方文档中去解决这个问题,改为 while 判断就可以,将代码中的条件改为while,查看运行结果成功解决了这个问题

image.png

Lock 实现生产者消费者问题

在之前的synchronized版本juejin.cn/post/703006… 里面使用wait()和 notify() 实现等待和通知

查看Lock的接口文档可以找到Condition,发现里面并不是使用 wait和notify 而是 await() 和 signal()

image.png

将上面的场景改为用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();
        }
    }
}
复制代码

查看运行结果加锁成功

image.png

但是从结果上来线程的分配结果是随机的看这个和之前用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();
        }
    }

}
复制代码

运行结果

image.png

通过设置不同的监视器进行通知,可以看到A、B、C是依次执行的

猜你喜欢

转载自juejin.im/post/7031194277933744164