方法1:最简单--利用LinkedBlockingQueue
队列具有先进先出的特点,成为经常应用于生产-消费者模式的数据结构。
1.将一个对象放到队列尾部,如果队列已满,就等待直到有空闲节点。 ——put()方法
2.从队列头部取一个对象,如果没有对象,就等待直到有对象可取。 ——take()方法
3.在存取队列的过程中,锁定队列对象,不允许其它线程访问队列。——使得它是线程安全的
下面的代码适用于多个生产者、多个消费者。
1 //Producer.java
2 importjava.util.concurrent.BlockingQueue;3
4 /*
5 * Usage example, based on a typical producer-consumer scenario.6 * Note that a BlockingQueue can safely be used with multiple7 * producers and multiple consumers.8 *
9 */
10 class Producer implementsRunnable {11 private BlockingQueuequeue;12
13 Producer(BlockingQueueq) {14 queue =q;15 }16
17 public voidrun() {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 newObject();32 }33 }
Pruducer.java
1 //Consumer.java
2 importjava.util.concurrent.BlockingQueue;3
4 class Consumer implementsRunnable {5 private BlockingQueuequeue;6
7 Consumer(BlockingQueueq) {8 queue =q;9 }10
11 public voidrun() {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 voidconsume(Object x) {24 }25 }
Consumer.java
1 importjava.util.concurrent.BlockingQueue;2 importjava.util.concurrent.LinkedBlockingQueue;3
4 classMain {5 public static voidmain(String[] args) {6 BlockingQueue q = new LinkedBlockingQueue();//SomeQueueImplementation();
7 Producer p = newProducer(q);8 Consumer c1 = newConsumer(q);9 Consumer c2 = newConsumer(q);10 Thread pth = newThread(p);11 pth.start();12 newThread(c1).start();13 newThread(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 importjava.util.concurrent.locks.Condition;3 importjava.util.concurrent.locks.Lock;4 importjava.util.concurrent.locks.ReentrantLock;5
6 public classProducerConsumer {7 private static final LinkedList buffer = new LinkedList();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 extendsThread {14 public voidrun() {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 extendsThread {36 public voidrun() {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 voidmain(String[] args){58 newConsumer().start();59 newProducer().start();60 }61 }
ProducerConsumer .java
关于Java线程的lock、condition、synchronized的小小总结:
1.synchronized简单易用,但是功能受局限,无法满足复杂的同步机制,比如“读者写者问题”:多个读者线程是不互斥的。
2.lock以更优雅的方式来解决线程同步,比如读写锁ReadWriteLock。
3.Condition解决线程间通信,为不同的线程建立不同的条件。
Condition可以替代传统的线程间通信,用await()替换wait(),用signal()替换notify(),用signalAll()替换notifyAll()。
——为什么方法名不直接叫wait()/notify()/nofityAll()?因为Object的这几个方法是final的,不可重写!
Condition实现了BlockingQueue的功能。
看看BlockingQueue的继承和实现。
一般的锁Lock的实现:注意ReadLock、WriteLock是内部的静态类,只有ReentrantReadWriteLock类可以调用。
读写的锁ReadWriteLock
思考:OS的信号量是什么,线程通信的方式有哪些?共享变量和消息传递。
信号量与互斥锁有什么联系?
By BYRHuangQiang2014-03-18 09:05:30