生产者与消费者问题

传统Synchronized版

/**
 * 线程之间的通信问题:生产者消费者问题! 等待唤醒,通知唤醒
 * 线程之间交替进行 线程A 线程B 同时操作一个变量number
 * 线程A +1
 * 线程B -1*/
public class Test01 {
    public static void main(String[] args) {
        Data data = new Data();
        
        new Thread(()->{
            for (int i=0; i<5; i++){
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();

        new Thread(()->{
            for (int i=0; i<5; i++){
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();

    }
}

//判断等待、业务、通知
//数据,资源类
class Data{

    private int number = 0;

    //加1操作
    public synchronized void increment() throws InterruptedException {
        if(number != 0){
            //等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"===>"+number);
        //通知其他线程
        this.notifyAll();
    }

    //减1操作
    public synchronized void decrement() throws InterruptedException {
        if (number == 0){
            //等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"===>"+number);
        //通知其他线程
        this.notifyAll();
    }
}

问题:上诉代码只是跑了A和B两个线程,请问,若再多几个线程,如四个线程,8个线程,线程是否还安全?
答案是不安全的。那应该如何解决呢?

虚假唤醒

jdk1.8官方文档对wait有一下的一个说明:



所以较上面的而言,为防止出现这种情况,我们应该在while循环中使用。

/**
 * 线程之间的通信问题:生产者消费者问题! 等待唤醒,通知唤醒
 * 线程之间交替进行 线程A 线程B 同时操作一个变量number
 * 线程A +1
 * 线程B -1*/
public class Test01 {
    public static void main(String[] args) {
        Data data = new Data();

        new Thread(()->{
            for (int i=0; i<5; i++){
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();

        new Thread(()->{
            for (int i=0; i<5; i++){
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();

        new Thread(()->{
            for (int i=0; i<5; i++){
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();

        new Thread(()->{
            for (int i=0; i<5; i++){
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();

    }
}

//判断等待、业务、通知
//数据,资源类
class Data{

    private int number = 0;

    //加1操作
    public synchronized void increment() throws InterruptedException {
        while (number != 0){
            //等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"===>"+number);
        //通知其他线程
        this.notifyAll();
    }

    //减1操作
    public synchronized void decrement() throws InterruptedException {
        while (number == 0){
            //等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"===>"+number);
        //通知其他线程
        this.notifyAll();
    }
}

JUC版


同样的,jdk官方文档给出了替代:



好了,我们直接上代码:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 线程之间的通信问题:生产者消费者问题! 等待唤醒,通知唤醒
 * 线程之间交替进行 线程A 线程B 同时操作一个变量number
 * 线程A +1
 * 线程B -1*/
public class Test02 {
    public static void main(String[] args) {
        Data2 data = new Data2();

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

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

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

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

//判断等待、业务、通知
//数据,资源类
class Data2 {

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

    //加1操作
    public void increment() {
        lock.lock();
        try {
            while (number != 0) {
                //等待
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName() + "===>" + number);
            //通知其他线程
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    //减1操作
    public void decrement() {
        lock.lock();
        try {
            while (number == 0) {
                //等待
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName() + "===>" + number);
            //通知其他线程
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}

Condition 实现精准通知唤醒

/**
 * 线程A、B、C按顺序执行
 * A 执行完通知唤醒 B
 * B 执行完通知唤醒 C
 * C 执行完通知唤醒 A*/
public class Test03 {
    public static void main(String[] args) {
        Data03 data = new Data03();
        new Thread(()->{
            for (int i=0; i<10; i++){
                data.printA();
            }
        },"A").start();
        new Thread(()->{
            for (int i=0; i<10; i++){
                data.printB();
            }
        },"B").start();
        new Thread(()->{
            for (int i=0; i<10; i++){
                data.printC();
            }
        },"C").start();
    }
}

//资源类
class Data03{

    final Lock lock = new ReentrantLock();
    final Condition condition1 = lock.newCondition();
    final Condition condition2 = lock.newCondition();
    final Condition condition3 = lock.newCondition();
    private int number = 1;//标志位 1->A 2->B 3->C

    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();
        }
    }
    
}

你可能感兴趣的:(生产者与消费者问题)