JAVA多线程(7):生产者和消费者问题详解

一、生产者和消费者问题

二、问题产生和解决

1.不考虑线程同步导致的问题

2.解决进度25%:只是将Clerk中两个方法添加上了Sychronized

3.解决进度50%:添加线程通信,生产者生产满了等待;消费者消费完了,等待。

4.解决进度75%:生产者如果添加200ms的延迟,会导致,消费进程无法停止。

5.解决进度100%:多个生产者和消费者导致问题优化


一、生产者和消费者问题

针对同一个对象,消费者消费和生产者生产,是同时进行。这样会导致多线程访问问题,需要使用线程同步,解决这个问题。如下进行分步分析,从问题开始,到使用sychronized或者lock关键字,解决生产者和消费者问题。

二、问题产生和解决

1.不考虑线程同步导致的问题

(1)查看类图
 JAVA多线程(7):生产者和消费者问题详解_第1张图片
(2)Clerk:共用类

package thread.thread.ProAndCon;

/**
 * 店员
 */
class Clerk{
    private int product=0;

    //进货
    public void get(){
        if(product>=10){
            System.out.println("产品已满!");
//            try {
//                this.wait();
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
        }else{
            System.out.println(Thread.currentThread().getName()+":"+ ++product);
//            this.notifyAll();
        }
    }

    //卖货
    public void sale(){
        if(product<=0){
            System.out.println("产品已没有了!");
//            try {
//                this.wait();
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
        }else {
            System.out.println(Thread.currentThread().getName()+":"+ --product);
//            this.notifyAll();
        }
    }
}

(3)生产者:Producer
实现功能:生产20个产品

package thread.thread.ProAndCon;

/**
 * 生产者
 */
class Producer implements Runnable{

    private Clerk clerk;

    public Producer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 0; i <= 20; i++) {
            clerk.get();
        }
    }
}

(4)消费者:Consumer
实现功能:消费20个产品

package thread.thread.ProAndCon;

/**
 * 消费者
 */
class Consumer implements Runnable{
    private Clerk clerk;

    public Consumer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 0; i <= 20; i++) {
            clerk.sale();
        }
    }
}


(5)测试类:TestProductorAndConsumer

package thread.thread.ProAndCon;

/**
 * Created by Administrator on 2020/1/16.
 */
public class TestProductorAndConsumer {

    public static void main(String[] args) {
        Clerk clerk=new Clerk();

        Producer producer=new Producer(clerk);
        Consumer consumer=new Consumer(clerk);

        new Thread(producer,"生产者A").start();
        new Thread(consumer,"消费者B").start();
    }
}


2.解决进度25%:只是将Clerk中两个方法添加上了Sychronized

(1)店员:Clerk

package thread.thread.ProAndCon_Sychronized;

/**
 * 店员
 */
class Clerk{
    private int product=0;

    //进货
    public synchronized void get(){
        if(product>=10){
            System.out.println("产品已满!");
//            try {
//                this.wait();
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
        }else{
            System.out.println(Thread.currentThread().getName()+":"+ ++product);
//            this.notifyAll();
        }
    }

    //卖货
    public synchronized void sale(){
        if(product<=0){
            System.out.println("产品已没有了!");
//            try {
//                this.wait();
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
        }else {
            System.out.println(Thread.currentThread().getName()+":"+ --product);
//            this.notifyAll();
        }
    }
}


(2)运行测试类会导致


/**
 * Created by Administrator on 2020/1/16.
 */
public class TestProductorAndConsumer {

    public static void main(String[] args) {
        Clerk clerk=new Clerk();

        Producer producer=new Producer(clerk);
        Consumer consumer=new Consumer(clerk);

        new Thread(producer,"生产者A").start();
        new Thread(consumer,"消费者B").start();
    }

问题:生产者生产满了10个,但是持续生产;消费者消费到0个,继续消费。
 

生产者A:1
生产者A:2
生产者A:3
生产者A:4
生产者A:5
生产者A:6
生产者A:7
生产者A:8
生产者A:9
生产者A:10
产品已满!
产品已满!
产品已满!
产品已满!
产品已满!
产品已满!
产品已满!
消费者B:9
消费者B:8
消费者B:7
消费者B:6
消费者B:5
消费者B:4
消费者B:3
消费者B:2
消费者B:1
消费者B:0
产品已没有了!
产品已没有了!
产品已没有了!
产品已没有了!
产品已没有了!
产品已没有了!
产品已没有了!
产品已没有了!
产品已没有了!
产品已没有了!
产品已没有了!
生产者A:1
生产者A:2
生产者A:3
生产者A:4


3.解决进度50%:添加线程通信,生产者生产满了等待;消费者消费完了,等待。

(1)店员:Clerk

package thread.thread.ProAndCon_Sychronized;

/**
 * 店员
 */
class Clerk{
    private int product=0;

    //进货
    public synchronized void get(){
        if(product>=10){
            System.out.println("产品已满!");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }else{
            System.out.println(Thread.currentThread().getName()+":"+ ++product);
            this.notifyAll();
        }
    }

    //卖货
    public synchronized void sale(){
        if(product<=0){
            System.out.println("产品已没有了!");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }else {
            System.out.println(Thread.currentThread().getName()+":"+ --product);
            this.notifyAll();
        }
    }
}

(2)运行测试类结果

生产者A:1
生产者A:2
生产者A:3
生产者A:4
生产者A:5
生产者A:6
生产者A:7
生产者A:8
生产者A:9
生产者A:10
产品已满!
消费者B:9
消费者B:8
消费者B:7
消费者B:6
消费者B:5
消费者B:4
消费者B:3
消费者B:2
消费者B:1
消费者B:0
产品已没有了!
生产者A:1
生产者A:2
生产者A:3
生产者A:4
生产者A:5
生产者A:6
生产者A:7
生产者A:8
生产者A:9
生产者A:10
消费者B:9
消费者B:8
消费者B:7
消费者B:6
消费者B:5
消费者B:4
消费者B:3
消费者B:2
消费者B:1
消费者B:0


4.解决进度75%:生产者如果添加200ms的延迟,会导致,消费进程无法停止。

(1)生产者添加200ms延迟(模拟生产环境)

package thread.thread.ProAndCon_Sychronized;

/**
 * 生产者
 */
class Producer implements Runnable{

    private Clerk clerk;

    public Producer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 0; i <= 20; i++) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.get();
        }
    }
}

(2)店员:Clerk修改生产者上限为1

package thread.thread.ProAndCon_Sychronized;

/**
 * 店员
 */
class Clerk{
    private int product=0;

    //进货
    public synchronized void get(){
        if(product>=1){
            System.out.println("产品已满!");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }else{
            System.out.println(Thread.currentThread().getName()+":"+ ++product);
            this.notifyAll();
        }
    }

    //卖货
    public synchronized void sale(){
        if(product<=0){
            System.out.println("产品已没有了!");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }else {
            System.out.println(Thread.currentThread().getName()+":"+ --product);
            this.notifyAll();
        }
    }
}

(3)运行测试代码:发现进程无法停止
 JAVA多线程(7):生产者和消费者问题详解_第2张图片

(4)原因分析

情景:当消费者剩下1次,生产者剩下2次。
消费者消费最后一次,product=0,等待wait;
生产者生产倒数第2次,product=1,并且通知消费者,消费者完成最后一次进程,但是此时product=1;
生产者进行最后一次生产,product进入wait,而没有其他人唤醒,所以程序无法结束。
 JAVA多线程(7):生产者和消费者问题详解_第3张图片

(5)解决问题:修改Clerk,把生产和消费的else去掉

package thread.thread.ProAndCon_SychronizedImp;

/**
 * 店员
 */
class Clerk{
    private int product=0;

    //进货
    public synchronized void get(){
        if(product>=1){
            System.out.println("产品已满!");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

//        else{
//            System.out.println(Thread.currentThread().getName()+":"+ ++product);
//            this.notifyAll();
//        }
        System.out.println(Thread.currentThread().getName()+":"+ ++product);
        this.notifyAll();
    }

    //卖货
    public synchronized void sale(){
        if(product<=0){
            System.out.println("产品已没有了!");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
//        else {
//            System.out.println(Thread.currentThread().getName()+":"+ --product);
//            this.notifyAll();
//        }
        System.out.println(Thread.currentThread().getName()+":"+ --product);
        this.notifyAll();
    }
}

测试后结果:顺利结束
JAVA多线程(7):生产者和消费者问题详解_第4张图片

5.解决进度100%:多个生产者和消费者导致问题优化

(1)测试添加C生产者和D消费者

package thread.thread.ProAndCon_SychronizedImp_muti;

/**
 * Created by Administrator on 2020/1/16.
 */
public class TestProductorAndConsumer {

    public static void main(String[] args) {
        Clerk clerk=new Clerk();

        Producer producer=new Producer(clerk);
        Consumer consumer=new Consumer(clerk);

        new Thread(producer,"生产者A").start();
        new Thread(consumer,"消费者B").start();
        new Thread(producer,"生产者C").start();
        new Thread(consumer,"消费者D").start();
    }
}

结果:

产品已没有了!
产品已没有了!
生产者A:1
消费者D:0
产品已没有了!
消费者B:-1
产品已没有了!
消费者D:-2
产品已没有了!
消费者B:-3
产品已没有了!
消费者D:-4
产品已没有了!
消费者B:-5
产品已没有了!
消费者D:-6
产品已没有了!
消费者B:-7
产品已没有了!
消费者D:-8
产品已没有了!
消费者B:-9
产品已没有了!
消费者D:-10
产品已没有了!
消费者B:-11
产品已没有了!
生产者C:-10
消费者B:-11
产品已没有了!
消费者D:-12
产品已没有了!
消费者B:-13
产品已没有了!
消费者D:-14
产品已没有了!
消费者B:-15
产品已没有了!
消费者D:-16
产品已没有了!
消费者B:-17
产品已没有了!
消费者D:-18
产品已没有了!
消费者B:-19
产品已没有了!
消费者D:-20
产品已没有了!
消费者B:-21
产品已没有了!
消费者D:-22
产品已没有了!
消费者B:-23
产品已没有了!
消费者D:-24
产品已没有了!
消费者B:-25
产品已没有了!
消费者D:-26
产品已没有了!
消费者B:-27
产品已没有了!
消费者D:-28
产品已没有了!
消费者B:-29
产品已没有了!
消费者D:-30
产品已没有了!
消费者B:-31
产品已没有了!
消费者D:-32
产品已没有了!
消费者B:-33
产品已没有了!
消费者D:-34
产品已没有了!
消费者B:-35
产品已没有了!
消费者D:-36
产品已没有了!
消费者B:-37
产品已没有了!
消费者D:-38
产品已没有了!
消费者B:-39
消费者D:-40
生产者A:-39
生产者C:-38
生产者A:-37
生产者C:-36
生产者A:-35
生产者C:-34
生产者A:-33
生产者C:-32
生产者A:-31
生产者C:-30
生产者A:-29
生产者C:-28
生产者A:-27
生产者C:-26
生产者A:-25
生产者C:-24
生产者A:-23
生产者C:-22
生产者A:-21
生产者C:-20
生产者A:-19
生产者C:-18
生产者A:-17
生产者C:-16
生产者A:-15
生产者C:-14
生产者A:-13
生产者C:-12
生产者A:-11
生产者C:-10
生产者A:-9
生产者C:-8
生产者A:-7
生产者C:-6
生产者A:-5
生产者C:-4
生产者A:-3
生产者C:-2
生产者A:-1
生产者C:0
Process finished with exit code 0

(2)导致问题:虚假唤醒
(3)解决:修改Clerk类的生产和消费方法的if判断语句,为while循环判断

package thread.thread.ProAndCon_SychronizedImp_muti;

/**
 * 店员
 */
class Clerk{
    private int product=0;

    //进货
    public synchronized void get(){
        while(product>=1){
            System.out.println("产品已满!");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

//        else{
//            System.out.println(Thread.currentThread().getName()+":"+ ++product);
//            this.notifyAll();
//        }
        System.out.println(Thread.currentThread().getName()+":"+ ++product);
        this.notifyAll();
    }

    //卖货
    public synchronized void sale(){
        while (product<=0){
            System.out.println("产品已没有了!");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
//        else {
//            System.out.println(Thread.currentThread().getName()+":"+ --product);
//            this.notifyAll();
//        }
        System.out.println(Thread.currentThread().getName()+":"+ --product);
        this.notifyAll();
    }
}

重新测试结果:

产品已没有了!
产品已没有了!
生产者A:1
消费者D:0
产品已没有了!
产品已没有了!
生产者C:1
消费者B:0
产品已没有了!
产品已没有了!
生产者A:1
消费者D:0
产品已没有了!
产品已没有了!
生产者C:1
消费者B:0
产品已没有了!
产品已没有了!
生产者C:1
消费者D:0
产品已没有了!
产品已没有了!
生产者A:1
消费者B:0
产品已没有了!
产品已没有了!
生产者C:1
消费者D:0
产品已没有了!
产品已没有了!
生产者A:1
消费者B:0
产品已没有了!
产品已没有了!
生产者A:1
消费者D:0
产品已没有了!
产品已没有了!
生产者C:1
消费者B:0
产品已没有了!
产品已没有了!
生产者A:1
消费者D:0
产品已没有了!
产品已没有了!
生产者C:1
消费者B:0
产品已没有了!
产品已没有了!
生产者C:1
消费者D:0
产品已没有了!
产品已没有了!
生产者A:1
消费者B:0
产品已没有了!
产品已没有了!
生产者C:1
消费者D:0
产品已没有了!
产品已没有了!
生产者A:1
消费者B:0
产品已没有了!
产品已没有了!
生产者C:1
消费者D:0
产品已没有了!
产品已没有了!
生产者A:1
消费者B:0
产品已没有了!
产品已没有了!
生产者C:1
消费者D:0
产品已没有了!
产品已没有了!
生产者A:1
消费者B:0
产品已没有了!
产品已没有了!
生产者C:1
消费者D:0
产品已没有了!
产品已没有了!
生产者A:1
消费者B:0
产品已没有了!
产品已没有了!
生产者C:1
消费者D:0
产品已没有了!
产品已没有了!
生产者A:1
消费者B:0
产品已没有了!
产品已没有了!
生产者A:1
消费者D:0
产品已没有了!
产品已没有了!
生产者C:1
消费者B:0
产品已没有了!
产品已没有了!
生产者A:1
消费者D:0
产品已没有了!
产品已没有了!
生产者C:1
消费者B:0
产品已没有了!
产品已没有了!
生产者A:1
消费者D:0
产品已没有了!
产品已没有了!
生产者C:1
消费者B:0
产品已没有了!
产品已没有了!
生产者C:1
消费者D:0
产品已没有了!
产品已没有了!
生产者A:1
消费者B:0
产品已没有了!
产品已没有了!
生产者C:1
消费者D:0
产品已没有了!
产品已没有了!
生产者A:1
消费者B:0
产品已没有了!
产品已没有了!
生产者C:1
消费者D:0
产品已没有了!
产品已没有了!
生产者A:1
消费者B:0
产品已没有了!
产品已没有了!
生产者A:1
消费者D:0
产品已没有了!
产品已没有了!
生产者C:1
消费者B:0
产品已没有了!
产品已没有了!
生产者A:1
消费者D:0
产品已没有了!
产品已没有了!
生产者C:1
消费者B:0
产品已没有了!
产品已没有了!
生产者C:1
消费者D:0
产品已没有了!
生产者A:1
消费者B:0

 

你可能感兴趣的:(java多线程)