博客信息

多线程间通信之生产者消费者

发布时间:『 2019-05-29 22:43』  博客类别:java基础  阅读(725)

将上篇博客的代码稍作修改

package com.javaxl.thread;

/**
 * @author 小李飞刀
 * @site www.javaxl.com
 * @company
 * @create  2019-05-29 21:34
 * <p>
 * 操作同一个资源
 * 资源中只有资源数量
 * 生产者对应一个生产的线程
 * 消费者对应一个消费的线程
 */
public class Demo4 {
    public static void main(String[] args) {
//        一个资源池
        Res r = new Res();
        Thread in = new Thread(new Input(r));
        Thread out = new Thread(new Output(r));
        in.start();
        out.start();

//        Thread in2 = new Thread(new Input(r));
//        Thread out2 = new Thread(new Output(r));
//        in2.start();
//        out2.start();
    }
}

class Res {
    private boolean flag = false;
    private int num = 0;

    /**
     * 往资源池中添加资源
     */
    public synchronized void set(){
        if (this.flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println(Thread.currentThread().getName()+"----------生产者---"+(++num));;
        this.flag = true;
        this.notify();
    }

    /**
     * 使用资源池中的资源
     */
    public synchronized void out(){
        if(!this.flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName()+"-消费者---"+num);;
        this.flag = false;
        this.notify();
    }

}

/**
 * 往资源池中生产
 */
class Input implements Runnable {
    private Res r;


    public Input(Res r) {
        this.r = r;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (r) {
                r.set();
            }
        }
    }
}

/**
 * 从资源池消费
 */
class Output implements Runnable {
    private Res r;

    public Output(Res r) {
        this.r = r;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (r) {
                r.out();
            }
        }
    }
}

上面这段代码,运行后会发现,如果只有一个生产者一个消费者,那么没有任何问题,资源存一个取一个;

如果有多个生产者多个消费者,那么又出现了并发问题,这个并发问题的产生是线程间的通信问题。

 

 

下面这个图是正常现象

小李飞刀_线程

 

多生产者多消费者线程操作的异常现象截图

小李飞刀_线程

 

下面来分析一下Res中的代码

小李飞刀_线程

 

这个代码一个生产者一个消费那么代码不会有并发问题

但是如果有多个生产者多个消费者,那么代码就会出现并发问题了

这里以两个生产者、两个消费者为例

那么会出现什么现象呢?

会出现生产出两个商品,而只消费了一个;

或者生产出一个商品,但是消费了两次的问题;

 

为什么会出现并发问题呢?

我们来分析上面代码,假设t1t2是两个生产者线程,t3t4是两个消费者线程;

1、t1作为生产者执行set方法,先 生产出一个商品,然后flag=true,线程放弃资格,进入等待状态;

2、T2再 进来,由于flag=truet2线程直接放弃资格,进入等待状态;

3、T3作为消费者执行out方法,因为flag=true,直接消费了一个商品,然后flag=false,线程放弃资格,进入等待状态,并且唤醒第一个等待的线程t1,线程t1获取资格;

4、T1执行线程内容无需做flag判断,这个时候t1线程生成出一个商品,这时flag=true,并且唤醒了t2;(因为t1被唤醒,所以具有抢夺锁资格的有t1t4线程,这里为了讲解并发现象,我们假设t1抢到了锁,获取了cpu的执行权

5、T2被唤醒也不需要做flag判断,t2直接就又生产出一个商品,然后flag=true,并且唤醒了t3t3线程也获取cpu的执行资格,这个时候又回到了最初的状态,四个线程都具备cpu执行权。但是从这一段分析中,我们发现第四、第五连续生产了两次商品,而只消费了一次;

 

反之亦然,也可能消费了两次,但是只生产了一次商品,这里就不做分析了;

通过上面的分析,我们知道造成其结果的根本原因在于,线程被唤醒后,不会再做if(flag)这个判断了;

那么我们的改进办法就是将if(flag)改成while(flag)

这样做就可以解决,线程被唤醒后不再做flag判断的问题,但是又出现了新的问题;

 

这时候会出现四个线程同时放弃资格,处于等待状态的情况;

其根本原因在于notify()每次只能唤醒本方线程,不能唤醒对方线程;

那么解决方法是:

notify()改为notifyAll()就可以了;

 

 

相关代码:

package com.javaxl.thread;

/**
 * @author 小李飞刀
 * @site www.javaxl.com
 * @company
 * @create  2019-05-29 21:34
 * <p>
 * 操作同一个资源
 * 资源中只有资源数量
 * 生产者对应一个生产的线程
 * 消费者对应一个消费的线程
 */
public class Demo4 {
    public static void main(String[] args) {
//        一个资源池
        Res r = new Res();
        Thread in = new Thread(new Input(r));
        Thread out = new Thread(new Output(r));
        in.start();
        out.start();

        Thread in2 = new Thread(new Input(r));
        Thread out2 = new Thread(new Output(r));
        in2.start();
        out2.start();
    }
}

class Res {
    private String name;
    private String sex;
    private boolean flag = false;
    private int num = 0;

    public Res(String name, String sex) {
        this.name = name;
        this.sex = sex;
    }

    public Res() {
    }

    /**
     * 往资源池中添加资源
     */
    public synchronized void set(){
        while (this.flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println(Thread.currentThread().getName()+"----------生产者---"+(++num));;
        this.flag = true;
        this.notifyAll();
    }

    /**
     * 使用资源池中的资源
     */
    public synchronized void out(){
        while (!this.flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName()+"-消费者---"+num);;
        this.flag = false;
        this.notifyAll();
    }

}

/**
 * 往资源池中生产
 */
class Input implements Runnable {
    private Res r;


    public Input(Res r) {
        this.r = r;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (r) {
                r.set();
            }
        }
    }
}

/**
 * 从资源池消费
 */
class Output implements Runnable {
    private Res r;

    public Output(Res r) {
        this.r = r;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (r) {
                r.out();
            }
        }
    }
}






关键字:     Java基础       多线程间通信       生产者消费者  

备案号:湘ICP备19000029号

Copyright © 2018-2019 javaxl晓码阁 版权所有