[基础]java实现生产者与消费者的三种方式

回顾java多线程安全这一part的时候,再学到生产者与消费者的问题。因此写一博客进行记录,同时希望能给在看博客的你提供一些帮助。

这篇文章主要介绍如何通过
  1. synchronized加锁的方式
  2. lock&&Condition的方式
  3. lock&&Condition精准通知与唤醒的方式来实现生产者和消费者
    这三种方式属于层层优化,且都是简单案例,读者可以在此基础上进行扩展。
实现生产者和消费者的主要思路和步骤
  1. 判断等待
  2. 执行业务
  3. 通知唤醒

场景:

  某线程生产一件物品,当该物品被消耗完时才会继续生产;同时另一线程消耗某件物品,当该物品数量大于零时才会继续消耗。通俗来说,就是生产一件物品与消耗一件物品有序交替执行。为了保证物品数量的线程安全,我们需要加锁。

方式一:synchronized

说明:该方式选择在资源类Data的生产和消费方法上加上synchronized从而声明为同步方法。这种方式不推荐。
  Data类为资源类,提供生产和消费的方法。对于生产方法来说,当不满足生产条件时,进入等待状态,在判断满足生产的条件下会执行生产业务,同时唤醒消费者进行消费。对于消费方法来说,当不满足消费条件时,进入等待状态,在判断满足消费的条件下会执行消费业务,同时唤醒生产者进行生产。

public class ProductDemo {

    public static void main(String[] args) {

        Data data = new Data();

        new Thread(()->{
            for(int i=0;i<4;i++){
                data.increase();
            }
        },"A").start();

        new Thread(()->{
            for(int i=0;i<4;i++){
                data.increase();
            }
        },"B").start();

        new Thread(()->{
            for(int i=0;i<4;i++){
                data.decrease();
            }
        },"C").start();

        new Thread(()->{
            for(int i=0;i<4;i++){
                data.decrease();
            }
        },"D").start();
    }
}


//判断等待;业务;通知
class Data{
    
    private int num=0;
    
    public synchronized void increase(){
        while (num!=0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        num++;
        System.out.println(Thread.currentThread().getName()+"==>"+num);
        this.notifyAll();
    }

    public synchronized void decrease(){
        while (num==0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        num--;
        System.out.println(Thread.currentThread().getName()+"==>"+num);
        this.notifyAll();
    }
}

方式二:lock&&Condition的普通方式

说明:Condition是java.util.concurrent.locks下的接口,基本的方法就是await()和signal()方法;Condition依赖于Lock接口,生成一个Condition的基本代码是lock.newCondition(),调用Condition的await()和signal()方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用。

public class NewProductDemo {

    public static void main(String[] args) {

        Data2 data = new Data2();

        new Thread(()->{
            for(int i=0;i<4;i++){
                data.increase();
            }
        },"A").start();

        new Thread(()->{
            for(int i=0;i<4;i++){
                data.increase();
            }
        },"B").start();

        new Thread(()->{
            for(int i=0;i<4;i++){
                data.decrease();
            }
        },"C").start();

        new Thread(()->{
            for(int i=0;i<4;i++){
                data.decrease();
            }
        },"D").start();
    }
}

//判断等待;业务;通知
class Data2{

    private int num=0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();

    public void increase(){
        lock.lock();
        try {
            while (num!=0) {
                condition.await();
            }
            num++;
            System.out.println(Thread.currentThread().getName()+"==>"+num);
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void decrease(){
        lock.lock();
        try {
            while (num==0) {
                condition.await();
            }
            num--;
            System.out.println(Thread.currentThread().getName()+"==>"+num);
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

方式三:lock&&Condition的精准通知与唤醒

说明:由于某些场景下我们要考虑到多个任务的顺序执行,或者需要精准唤醒指定方法,可以考虑使用这种方式。

public class LatestProductDemo {
    public static void main(String[] args) {

        Data3 data = new Data3();

        new Thread(()->{
            for(int i=0;i<4;i++){
                data.showA();
            }
        },"A").start();

        new Thread(()->{
            for(int i=0;i<4;i++){
                data.showB();
            }
        },"B").start();

        new Thread(()->{
            for(int i=0;i<4;i++){
                data.showC();
            }
        },"C").start();
    }
}

//判断等待;业务;通知
class Data3{

    private int num=1;
    Lock lock = new ReentrantLock();
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();
    Condition condition3 = lock.newCondition();

    public void showA(){
        lock.lock();

        try {
            while (num!=1) {
                condition1.await();
            }
            num=2;
            System.out.println(Thread.currentThread().getName()+"==>"+num);
            condition2.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void showB(){
        lock.lock();

        try {
            while (num!=2) {
                condition2.await();
            }
            num=3;
            System.out.println(Thread.currentThread().getName()+"==>"+num);
            condition3.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void showC(){
        lock.lock();
        try {
            while (num!=3) {
                condition3.await();
            }
            num=1;
            System.out.println(Thread.currentThread().getName()+"==>"+num);
            condition1.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

猜你喜欢

转载自blog.csdn.net/fallwind_of_july/article/details/107913547