• Java实现生产者消费者


    方法1:最简单--利用LinkedBlockingQueue

    队列具有先进先出的特点,成为经常应用于生产-消费者模式的数据结构。
    1.将一个对象放到队列尾部,如果队列已满,就等待直到有空闲节点。 ——put()方法
    2.从队列头部取一个对象,如果没有对象,就等待直到有对象可取。 ——take()方法
    3.在存取队列的过程中,锁定队列对象,不允许其它线程访问队列。——使得它是线程安全的

    下面的代码适用于多个生产者、多个消费者。

     1 //Producer.java
     2 import java.util.concurrent.BlockingQueue;
     3 
     4 /*
     5  *  Usage example, based on a typical producer-consumer scenario.
     6  * Note that a <tt>BlockingQueue</tt> can safely be used with multiple
     7  * producers and multiple consumers.
     8  * <pre>
     9  */
    10 class Producer implements Runnable {
    11     private BlockingQueue<Object> queue;
    12 
    13     Producer(BlockingQueue<Object> q) {
    14         queue = q;
    15     }
    16 
    17     public void run() {
    18         try {
    19             while (true) {
    20                 queue.put(produce());
    21                 Thread.sleep(1);
    22                 System.out.println("+1 Produce an Object:" + "生产者size:"
    23                         + queue.size());
    24             }
    25         } catch (InterruptedException ex) {
    26             System.out.println("生产者结束!");
    27         }
    28     }
    29 
    30     Object produce() {
    31         return new Object();
    32     }
    33 }
    Pruducer.java
     1 //Consumer.java
     2 import java.util.concurrent.BlockingQueue;
     3 
     4 class Consumer implements Runnable {
     5     private BlockingQueue<Object> queue;
     6 
     7     Consumer(BlockingQueue<Object> q) {
     8         queue = q;
     9     }
    10 
    11     public void run() {
    12         try {
    13             while (true) {
    14                 consume(queue.take());
    15                 Thread.sleep(10);
    16                 System.out.println("-1 Consumer an Object:" + "size:"+ queue.size());
    17             }
    18         } catch (InterruptedException ex) {
    19             System.out.println("消费者结束!");
    20         }
    21     }
    22 
    23     void consume(Object x) {
    24     }
    25 }
    Consumer.java
     1 import java.util.concurrent.BlockingQueue;
     2 import java.util.concurrent.LinkedBlockingQueue;
     3 
     4 class Main {
     5     public static void main(String[] args) {
     6         BlockingQueue<Object> q = new LinkedBlockingQueue<Object>();// SomeQueueImplementation();
     7         Producer p = new Producer(q);
     8         Consumer c1 = new Consumer(q);
     9         Consumer c2 = new Consumer(q);
    10         Thread pth = new Thread(p);
    11         pth.start();
    12         new Thread(c1).start();
    13         new Thread(c2).start();
    14         try {
    15             Thread.sleep(50);
    16             pth.interrupt();
    17         } catch (InterruptedException e) {
    18         }
    19     }
    20 }
    Main.java

    方法2:不采用BlockingQueue,利用lock、Condition来实现

    思路:

    利用可重用的LinkedList来存放资源,生产者addLast(),消费者removeFirst()。

    使用ReentrantLock()来实现消费者和生产者的同步;//可重用的互斥锁

    思考:为什么使用了lock后,还使用了它的2个newCondition,这样做有什么好处?

     1 import java.util.*;
     2 import java.util.concurrent.locks.Condition;
     3 import java.util.concurrent.locks.Lock;
     4 import java.util.concurrent.locks.ReentrantLock;
     5 
     6 public class ProducerConsumer {
     7     private static final LinkedList<Integer> buffer = new LinkedList<Integer>();
     8     private static final int BUFFERSIZE = 10;
     9     private static Lock lock = new ReentrantLock();//可重用的互斥锁
    10     private static Condition NotFull = lock.newCondition();
    11     private static Condition NotEmpty = lock.newCondition();
    12 
    13     static class Producer extends Thread {
    14         public void run() {
    15             while (true) {
    16                 // lock
    17                 lock.lock();
    18                 if (buffer.size() == BUFFERSIZE) {
    19                     System.out.println("BUffer is fulls");
    20                     try {
    21                         NotFull.await();
    22                     } catch (InterruptedException e) {
    23                         e.printStackTrace();
    24                     }
    25                 } else {
    26                     buffer.addLast(1);
    27                     NotEmpty.signal();
    28                 }
    29                 lock.unlock();
    30                 // unlock
    31             }
    32         }
    33     }
    34     
    35     static class Consumer extends Thread {
    36         public void run() {
    37             while (true) {
    38                 // lock
    39                 lock.lock();
    40                 if (buffer.size() == 0) {
    41                     System.out.println("BUffer is empty");
    42                     try {
    43                         NotEmpty.await();
    44                     } catch (InterruptedException e) {
    45                         e.printStackTrace();
    46                     }
    47                 } else {
    48                     buffer.removeFirst();
    49                     NotFull.signal();
    50                 }
    51                 // unlock
    52                 lock.unlock();
    53             }
    54         }
    55     }
    56     
    57     public static void main(String[] args){
    58         new Consumer().start();
    59         new Producer().start();
    60     }
    61 }
    ProducerConsumer .java

    关于Java线程的lock、condition、synchronized的小小总结:

    1.synchronized简单易用,但是功能受局限,无法满足复杂的同步机制,比如“读者写者问题”:多个读者线程是不互斥的。

    2.lock以更优雅的方式来解决线程同步,比如读写锁ReadWriteLock。

    3.Condition解决线程间通信,为不同的线程建立不同的条件。

    推荐博文:Java线程-锁机制

    Condition可以替代传统的线程间通信,用await()替换wait(),用signal()替换notify(),用signalAll()替换notifyAll()。

         ——为什么方法名不直接叫wait()/notify()/nofityAll()?因为Object的这几个方法是final的,不可重写!

    Condition实现了BlockingQueue的功能。

    看看BlockingQueue的继承和实现。

    一般的锁Lock的实现:注意ReadLock、WriteLock是内部的静态类,只有ReentrantReadWriteLock类可以调用。

    读写的锁ReadWriteLock

    思考:OS的信号量是什么,线程通信的方式有哪些?共享变量和消息传递。

            信号量与互斥锁有什么联系?

            参考资料 

    By BYRHuangQiang 2014-03-18 09:05:30 

  • 相关阅读:
    Spring Cloud
    如何修改容器时间而不改变宿主机时间?
    消息中间件 RabbitMQ 入门篇
    CentOS7下NFS服务安装及配置固定端口
    查看ssh有没有被黑的IP
    JVM常用命令和性能调优建议
    nfs高可用
    kafka集群部署以及单机部署
    OSGI企业应用开发(十三)OSGI Web应用开发(二)
    OSGI企业应用开发(十二)OSGI Web应用开发(一)
  • 原文地址:https://www.cnblogs.com/byrhuangqiang/p/3606349.html
Copyright © 2020-2023  润新知