消费者生产者代码之---一步一步带你写

面试时可能会面到生产者消费者模式,我们用java代码来实现下,一步一步思路给大家列出来,方便理解记忆

单生产者消费者模式

  1. 先想象一下,我们要弄一个仓库,这个仓库有两个暴露出去的方法put 和 pull ,一个是将产品放入(生产)自己的仓库,一个是从自己的仓库消费(取出)一个,还有一个属性count,表示仓库的容量

到这里请用自己代码实现下上面的功能,再看下面的,下面同理
下面给出上面的实现

public class Factory {
    
    

    int count = 0;

    public void put() {
    
    
        count++;
    }

    public void pull() {
    
    
        count--;
    }

}
  1. 很简单吧,下面再来想一下,在我们count++,和count–的时候,消费者和生产者同时对count进行操作,所以会有线程安全问题,加锁
public class Factory {
    
    

    int count = 0;
	
    public void put() {
    
    
    	synchronized (this) {
    
    
       		count++;
        }
    }

    public void pull() {
    
    
       synchronized (this) {
    
    
       		count--;
        }
    }

}

这样同时只会有一个线程去操作我们的数据了,保证了安全,再来想
3. 我们的仓库有一个最大容量,因为生产着放满了就得停止,让消费者继续消费,消费者消费到0的时候就该停止,再让生产者生产,我们用wait方法来模拟生生产满了或者消费完了之后进行的停止操作,notifyAll()来模拟生产满了之后让消费者醒来去消费的操作,最大容量设置为10,之后我们的代码是

public class Factory {
    
    

    int count = 0;
	
    public void put() {
    
    
    	synchronized (this) {
    
    
    		//判断是否满了
    		if(count == 10){
    
    
    			//在停止之前唤醒所有等待的线程
    			notifyAll();
    			//停止生产,进入等待状态
				wait();
			}
       		count++;
        }
    }

    public void pull() {
    
    
       synchronized (this) {
    
    
       		//判断是否满了
    		if(count == 10){
    
    
    			//在停止之前唤醒所有等待的线程
    			notifyAll();
    			//停止生产,进入等待状态
				wait();
			}
			count--;
        }
    }

}
  1. 大家,至此我们的简单的生产者消费者模型就写完了!没错!就这么简单
    我们来进行代码运行
    在这里插入图片描述
  2. wait报错,下图加上try catch(不知不觉代码就多了起来,其实看到一大坨代码不要怕,就那几步有用)
public class Factory {
    
    

    int count = 0;

    public void put() {
    
    
        synchronized (this) {
    
    
            //判断是否满了
            if(count == 10){
    
    
                //在停止之前唤醒所有等待的线程
                notifyAll();
                //停止生产,进入等待状态
                try {
    
    
                    wait();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
            count++;
        }
    }

    public void pull() {
    
    
        synchronized (this) {
    
    
            //判断是否满了
            if(count == 10){
    
    
                //在停止之前唤醒所有等待的线程
                notifyAll();
                //停止生产,进入等待状态
                try {
    
    
                    wait();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
            count--;
        }
    }
  1. 发现没有输出控制台看不到结果,加上输出,并用main方法运行起来
public class Factory {
    
    

    int count = 0;

    public void put() {
    
    
        synchronized (this) {
    
    
            //判断是否满了
            if(count == 10){
    
    
             	System.out.println(Thread.currentThread().getName() + "---容量已满,阻塞");
                //在停止之前唤醒所有等待的线程
                notifyAll();
                //停止生产,进入等待状态
                try {
    
    
                    wait();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
     		count++;
     		System.out.println(Thread.currentThread().getName() + "---放入了第" + count);
        }
    }

    public void pull() {
    
    
        synchronized (this) {
    
    
            //判断是否满了
            if(count == 0){
    
    
            	System.out.println(Thread.currentThread().getName() + "容量已清空,阻塞");
                //在停止之前唤醒所有等待的线程
                notifyAll();
                //停止生产,进入等待状态
                try {
    
    
                    wait();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + "拿走了第" + count);
            count--;
        }
		public static void main(String[] args) {
    
    
        	Factory factory = new Factory();
        
       		new Thread(()->{
    
    
            	factory.put();
        	},"生产者1").start();
        
        	new Thread(()->{
    
    
            	factory.pull();
        	},"消费者1").start();
    	}
    }

结束
请读者完全理解并写出以上代码以后在进行一下步

多生产者多消费者生产模式

再上面代码的基础上如果main方法这样写

public static void main(String[] args) {
    
    
        Factory factory = new Factory();
        new Thread(()->{
    
    
            factory.put();
        },"生产者1").start();
        new Thread(()->{
    
    
            factory.put();
        },"生产者2").start();

        new Thread(()->{
    
    
            factory.pull();
        },"消费者1").start();
        new Thread(()->{
    
    
            factory.pull();
        },"消费者2").start();

    }

这样写就有了两个生产者和两个消费者去操作数据,即多生产多消费,那么我们写的代码来执行会有什么问题呢?,
来看解析
假设此时仓库容量为2,

  1. 1号2号代表生产者,3号4号代表消费者
  2. 一开始count为0,3号先进入同步块执行,判断此时count为0,阻塞,注意此时阻塞在了这里,活着的线程为1,2,4
    在这里插入图片描述
  3. 然后1号连续生产了2个,在生产第三个的时候同样阻塞,在阻塞前唤醒了其他所有线程,此时活着的线程为2,3,4
  4. 4号线程消费了2次后count为0,此时cpu刚好切换到了3号,然后3号从上图位置继续执行,不判断count是否为0,直接进行count–操作,此时count为-1!

出现以上问题我们只需要把if换成while
在这里插入图片描述
换成while后,3号线程被唤醒后会再去判断标志,为0,继续等待,这样就解决了多生产多消费的问题

多生产多消费优化

在解决了以上问题后,我们接下来看一种情况
3号消费线程一开始判断count为0,唤醒其他线程,自己阻塞,然后cpu切换到4号,4号判断count为0,唤醒其他线程,自己阻塞,然后cpu切换到3号。。。。。。这样循环好多次之后才切换到生产者,这样影响了效率
在这里插入图片描述
我们给出解决方案,利用lock锁的condition对象进行操作
在这里插入图片描述
他是怎么做到的?,lock锁是1.5版本后引入的,需要我们手动的加锁解锁,并提供了一个condition对象进行分类唤醒,也就是说,生产者方有一个condition1,消费者方有一个condition2,这样容器满了我们可以选择性的去唤醒对应方
附上代码

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

public class Factory{
    
    

    int count = 0 ;
    Lock lock = new ReentrantLock();
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();
    public void put(){
    
    

        while(true){
    
    
            try {
    
    
                Thread.sleep(100);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
                lock.lock();

                    while (count == 10) {
    
    
                        System.out.println(Thread.currentThread().getName() + "---容量已满,阻塞");
                        condition2.signalAll();
                        try {
    
    
                            condition1.await();
                        } catch (InterruptedException e) {
    
    
                            e.printStackTrace();
                        }
                    }
                    count++;
                    System.out.println(Thread.currentThread().getName() + "---放入了第" + count);

                lock.unlock();

        }

    }
    public void pull(){
    
    
        try {
    
    
            Thread.sleep(100);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        while(true){
    
    
            lock.lock();
                while (count == 0) {
    
    

                    System.out.println(Thread.currentThread().getName() + "容量已清空,阻塞");
                    condition1.signalAll();
                    try {
    
    
                        condition2.await();
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName() + "拿走了第" + count);
                count--;

            lock.unlock();
        }

    }

    public static void main(String[] args) {
    
    
        Factory factory = new Factory();
        new Thread(()->{
    
    
            factory.put();
        },"生产者1").start();
        new Thread(()->{
    
    
            factory.put();
        },"生产者2").start();

        new Thread(()->{
    
    
            factory.pull();
        },"消费者1").start();
        new Thread(()->{
    
    
            factory.pull();
        },"消费者2").start();

    }

}

这也是我们最终的生产者消费者模式示例代码,关于Lock锁,右转百度2333

猜你喜欢

转载自blog.csdn.net/lioncatch/article/details/106206448