Java面试——写一个生产者与消费者

更多内容,前往个人博客

一、通过synchronize 中的 wait 和 notify 实现


【1】我们可以将生产者和消费者需要的方法写在公共类中

 1 package com.yintong.concurrent;
 2 
 3 import java.util.LinkedList;
 4 
 5 public class Concurrentcomm {
 6     //常量
 7     private static int MAX_VALUE = 10;
 8     //可以理解为缓存
 9     LinkedList<String> linkedList = new LinkedList<>();
10     Object object = new Object();
11     /*
12      * 生产者方法
13      */
14     public void product() throws Exception {
15         synchronized(linkedList) {
16             while(MAX_VALUE == linkedList.size()) {
17                 System.out.println("仓库已满,【生产者】: 暂时不能执行生产任务!");
18                 linkedList.wait();
19             }
20             linkedList.push("   李四    ");
21             System.out.println("【生产者】:生产了一个产品\t【现仓储量为】:" + linkedList.size());
22             linkedList.notifyAll();
23         }
24     }
25     /*
26      * 消费者方法
27      */
28     public void customer() throws Exception {
29         /*
30          * 根据jdk的void notifyAll()的描述,“解除那些在该对象上调用wait()方法的线程的阻塞状态。该方法只能在同步方法或同步块内部调用。
31          * 如果当前线程不是对象所得持有者,
32          * 该方法抛出一个java.lang.IllegalMonitorStateException 异常”
33          * so我们使用同一把锁
34          */
35         synchronized (linkedList) {
36                         //多线程判断中使用 while 不要使用 if 否则会出现虚假唤醒问题
37             while(linkedList.size() == 0) {
38                 System.out.println("仓库无货,【消费者】: 暂时不能执行消费任务!");
39                 linkedList.wait();
40             }
41             linkedList.pop();
42             System.out.println("【消费者】:消费了一个产品\t【现仓储量为】:" + linkedList.size());
43             linkedList.notifyAll();
44         }
45     }
46 }

【2】在 main 函数中调用生产者和消费者方法,并加限制即可

 1 /**
 2  * @author zzx
 3  * @desc 生产者与消费者
 4  *
 5  */
 6 public class Concurrent {
 7     //常量
 8     private static int MAX_VALUE = 100;
 9 
10     public static void main(String[] args) {
11         Concurrentcomm con = new Concurrentcomm();
12         new Thread(new Runnable() {
13 
14             @Override
15             public void run() {
16                 try {
17                     for (int i = 0; i < MAX_VALUE; i++) {
18                         Thread.sleep(0);
19                         con.product();
20                     }
21                 } catch (Exception e) {
22                     // TODO Auto-generated catch block
23                     e.printStackTrace();
24                 }
25             }
26         }).start();
27         // 消费者
28         new Thread(new Runnable() {
29 
30             @Override
31             public void run() {
32                 try {
33                     Thread.sleep(10);
34                     for (int i = 0; i < MAX_VALUE; i++) {
35                         con.customer();
36                     }
37                 } catch (Exception e) {
38                     e.printStackTrace();
39                 }
40             }
41         }).start();
42     }
43 }

【3】简单的生产者与消费者模式就完成了,可以看下运行的结果

 

二、通过 Lock 中的 await 与 signalAll 实现


【1】我们将公共的属性和方法放在 Resouce 类中,在资源类中使用 Lock 中的 lock()进行加锁,控制并发操作。使用 await()方法阻塞线程。使用 signalAll()唤醒线程。

 1 /**
 2  * 通过 Lock 实现生产者与消费者
 3  * 资源类:将公共的资源放在一个单独的类中,可以将其看做一个产品,自身就就有生产和消费的能力(方法)
 4  */
 5 public class ProductAndConsumer {
 6     public static void main(String[] args) {
 7         Resouce resouce = new Resouce();
 8         //生产者
 9         new Thread(()->{
10             for (int i=1;i<=5;i++) {
11                 resouce.product();
12             }
13         },String.valueOf("生产者")) .start();
14 
15         //消费者
16         new Thread(()->{
17             for (int i=1;i<=5;i++){
18                 resouce.consumer();
19             }
20         },String.valueOf("消费者")).start();
21     }
22 }
23 //资源类
24 class Resouce {
25     private int MAX_VALUE = 3;
26     private int MIN_VALUE = 0;
27     private int number = 0;
28     private Lock lock = new ReentrantLock();
29     private Condition condition = lock.newCondition();
30 
31     //生产者
32     public void product(){
33         try {
34             lock.lock();
35             //如果生产的数量大于最大值则阻塞
36             while(number >= MAX_VALUE){
37                 condition.await();
38             }
39             number++;
40             System.out.println("【生产者】:生产了一个产品\t【现仓储量为】:" + number);
41             condition.signalAll();
42         } catch (InterruptedException e) {
43             e.printStackTrace();
44         }finally {
45             lock.unlock();
46         }
47     }
48 
49     //消费者
50     public void  consumer(){
51         try {
52             lock.lock();
53             //如果消费的值=0则阻塞
54             while(number <= MIN_VALUE){
55                 condition.await();
56             }
57             number--;
58             System.out.println("【消费者】:消费了一个产品\t【现仓储量为】:" + number);
59             condition.signalAll();
60         } catch (InterruptedException e) {
61             e.printStackTrace();
62         }finally {
63             lock.unlock();
64         }
65     }
66 }

【2】输出结果展示:
 

三、synchronized 和 Lock 的区别


【1】原始构成:synchronized 是关键字属于 JVM 层面。底层通过 monitorenter(进入)monitorexit(退出)实现。底层是通过 monitor 对象完成,其实 wait/notify 等方法也依赖于 monitor 对象,只有在同步块或方法中才能调用 wait/notify 等方法。Lock 是具体类(java.util.concurrent.locks.Lock)是 API 层面的锁。
【2】使用方法:synchronized 不需要用户手动释放锁,当 synchronized 代码执行完后,系统会自动释放锁。ReentrantLock 则需要用户手动释放锁,若未主动释放锁,就可能导致出现死锁的现象。
【3】等待是否中断:synchronized 不可中断,除非抛出异常或者正常运行完成。ReentrantLock 可中断,1)、设置超时时间 tryLock(long timeout,TimeUnit unit) 2)、lockInterruptibly() 放在代码块中,调用 interrupt() 方法可中断。
【4】加锁是否公平:synchronized 非公平锁。ReentrantLock 两者都可以,默认是非公平锁,构造方法可以传入 boolean 值,true 为公平锁,false 为非公平锁。
【5】锁绑定多个条件 Condition:synchronized 没有。ReentrantLock 用来实现分组唤醒需要唤醒的线程们,可以精确唤醒,而不是像 synchronized 要么随机唤醒一个线程要么唤醒全部线程。

四、通过阻塞队列实现生产者与消费者


【1】通过blockQueue 中的 put/take 方法实现生产者与消费者,具体实现如下:当生产者使用put 生产到指定的队列大小3时,就会阻塞当前线程。这是消费者线程会通过 take 方法消费队列中的消息。当队列中没有消息时,会阻塞,直到有消息消费。

 1 public class BlockProductConsumer {
 2     public static void main(String[] args) {
 3         MyResouce resouce = new MyResouce(new ArrayBlockingQueue(3));
 4         //生产者线程
 5         new Thread(()->{
 6             for(int i=1;i<=10;i++){
 7                 resouce.product();
 8             }
 9         },"生产者").start();
10 
11         //消费者线程
12         new Thread(()->{
13             for(int i=1;i<=10;i++){
14                 try {
15                     resouce.consumer();
16                 } catch (InterruptedException e) {
17                     e.printStackTrace();
18                 }
19             }
20         },"消费者").start();
21 
22         try {
23             TimeUnit.SECONDS.sleep(1);
24             resouce.stop();
25         } catch (InterruptedException e) {
26             e.printStackTrace();
27         }
28     }
29 }
30 
31 
32 /**
33  * 公共资源类
34  */
35 class MyResouce{
36     //标记 while 无限循环
37     private volatile boolean FLAG = true;
38     //队列中存入的数值
39     private AtomicInteger atomicInteger = new AtomicInteger();
40     //组合一个阻塞队列,通过构造器传入
41     private BlockingQueue blockingQueue;
42     public MyResouce(BlockingQueue blockingQueue) {
43         this.blockingQueue = blockingQueue;
44     }
45 
46     //生产者
47     public void product(){
48         try {
49             while (FLAG){
50                 blockingQueue.put(String.valueOf(atomicInteger.incrementAndGet()));
51                 System.out.println("生产者生产第"+blockingQueue.size()+"个产品");
52             }
53         } catch (InterruptedException e) {
54             e.printStackTrace();
55         }
56     }
57 
58     //消费者
59     public void consumer() throws InterruptedException {
60         while (FLAG){
61             blockingQueue.take();
62             System.out.println("消费者消费第"+(blockingQueue.size()+1)+"个产品");
63         }
64     }
65 
66     public void stop(){
67         FLAG = false;
68         System.out.println("========================");
69     }
70 }

【2】效果展示:
 

posted @ 2020-11-19 19:42  Java程序员进阶  阅读(2254)  评论(0编辑  收藏  举报