多线程---多生产者与多消费者 (if/while之间的区别)

938 阅读3分钟

引入

在这里插入图片描述 这是一个线程同步问题,生产者和消费者共享一个资源,并且生产者相互依赖,互为条件。

  • 对于生产者,没有生产产品之前,要通知消费者等待,生产了产品后,要通知消费者可以消费
  • 对于消费者,在消费之后,要通知生产者自己结束了消费,可以生产产品了
  • synchronized 关键字可以阻止并发地更新同一个资源,可以实现同步,但是不能用来实现不同线程间的消息传递,也即是通信
wait让线程一直等待,直到其它线程通知,会释放锁,可以传递一个类型为long的参数,表示指定等待的毫秒数
notify唤醒一个处于等待状态的线程
notifyAll唤醒同一个对象上所有调用wait方法的线程,优先级高的优先调度
上述方法均属于Object类的方法,只能在同步方法或者同步代码中使用,否则会抛出InterruptedException异常

并发协作模型“生产者/消费者模式” 管程法

  • 生产者:负责生产数据的模块
  • 消费者:负责处理数据的模块
  • 缓冲区:消费者不能直接使用生产者的数据,要通过缓冲区将生产者的数据放入到缓冲区中,消费者再从缓冲区里面拿出数据

缓冲区

class Container<T>{
    private LinkedList<T> list = new LinkedList<T>();
    private final int MAX = 100;
    private  int count = 0;

    //往缓冲区里面添加商品
    public synchronized void put(T t){
        while(list.size() == MAX){
            try{
                this.wait();;
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
        list.add(t);
        System.out.println(Thread.currentThread().getName() + t);
        this.notifyAll();
    }
    
    //从缓冲区里面消费商品
    public synchronized void get()  {
        while (list.size() == 0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        String  t = (String) list.removeFirst();
        System.out.println(Thread.currentThread().getName() + t);
        this.notifyAll();
    }
}

消费者

class Customer implements Runnable{
    Container container;

    public Customer(Container container) {
        this.container = container;
    }

    @Override
    public void run() {
        while (true){
            container.get();
        }
    }
}

生产者

class Productor implements Runnable{
    Container container;
    String name;
    
    public Productor(Container container,String name) {
        this.container = container;
        this.name = name;
    }

    @Override
    public void run() {
        while (true){
            container.put(name);
        }
    }
}

主函数

public class testThread{
    public static void main(String[] args) {
    
        Container<String> container = new Container();
        
        new Thread(new Productor(container,"桃子"),"生产者1").start();
        new Thread(new Customer(container),"消费者1").start();
        new Thread(new Customer(container),"消费者2").start();
        new Thread(new Productor(container,"雪梨"),"生产者2").start();

    }
}

注意(虚假唤醒)

在多生产者的情况下我们判断的时候要用 while 来做判断而不是if

我们假设线程的调度是这样的,我们假设缓冲区最大容量为7,此时是已经达到最大容量,生产者线程都会被阻塞(此时生产者线程A已经经过 if 判断,在wait处等待,生产者线程B也通过了 if判断,在wait处等待),当消费者通知生产者线程可以生产时(我们调用的是notifyAll方法),所有被阻塞的生产者线程都会被唤醒(A和B都会被唤醒),假如是生产者线程A获得了执行的机会,即获得了锁,而生产者线程B 继续在锁池中等待,此时生产者线程B是醒着的。假设生产者又把缓冲区生产满了,并且释放锁,生产者线程B获得锁,在这种情况下,B已经通过了if判断,即使缓冲区已经满了,但是B一样可以往里面添加数据,就会造成错误。假如我们用的是while。