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 

posted on 2014-03-17 22:33  BYRHuangQiang  阅读(1417)  评论(0编辑  收藏  举报

导航