java学习之多生产者和多消费者
在上一节当中我们说道了,java多线程当中单个消费者对应单个生产者的关系。这个时候有几个点需要注意一下,第一个就是把if判断flag的语句改成while这样能够避免,比如如果我们这个时候用if的话判断完为真之后,线程就睡过去了,但是当下一次线程notify的时候,这个时候生产者还有消费者,也就是一个锁上面的线程拥有线程苏醒的机会是等同的。这个时候,如果生产者冻结之后,紧接着notify的话这个时候苏醒的还可能是生产者,这是我们不愿意看到的。那我们应该如何来处理呢,这个时候我们就应该把if改成while因为while是个循环,当下一次该线程苏醒的时候还应到回过头来判断,flag标记,这样就避免了生产者自我唤醒的这种情况。
但是现在问题变了,就是这个时候有多个生产者还有多个消费者,我们知道当只有一个生产者还有一个消费者的时候,至少会唤醒其中的一个,那么要是多个生产者还有多个消费者呢,我们可以这样想一下,比如说这个时候有两个生产者A和B,还有两个消费者C和D,此时A生产完了之后,这个时候A就开始唤醒了,假如A唤醒了C这个时候C开始生产消费产品,消费完了之后开始通知其他线程,这个时候假如被唤醒的是D线程,因为flag是假的证明已经没有东西可消费了,这个时候他又冻结。那么这个时候其他线程就都被冻结了,这个时候就形成了死锁现象,如果要避免死锁现象的话,java又为我们提供了一个方法,叫做notifyAll()方法,这个方法的作用在于能够把所有的线程都唤醒,这样就能保证消费者还有生产者至少可以唤醒双方的一个线程,保证程序的正常运行。这样做的一个缺点就是其中还有一个属于己方的线程被唤醒,这样就浪费了资源。于是在jdk更新的时候,就为我们提供了一个新的api能够显式的来操作锁还有唤醒冻结线程,这个线程不再核心lang包当中,这个api在java.util.concurrent.locks这个包当中为我们提供了两个接口,一是Lock接口还有一个是Condition接口。Lock接口扩展的方法有lock()方法还有一个unlock方法,这里需要注意的一点是,无论如何一定要释放锁,这个时候就用到了前面异常所讲到的finally语句,无论如何一定要执行。Lock当中还扩展了一个方法就是newCondition()这个方法,这个方法的作用是给一个锁定义多个消息传递的方式也就是给线程绑定多个监视器,比如说生产者只唤醒消费者的线程,而不唤醒生产者的线程,同理消费者也是如此。同时在Lock当中等待的方法不是wait()方法,唤醒的方法也不是notify()方法,这两个方法分别对应Lock当中的await()方法和signal()方法。
我们综合上面所讲来用代码体现下:
1 import java.util.concurrent.locks.*; 2 class KaoYa 3 { 4 5 int num = 1; 6 7 Lock l = new ReentrantLock(); 8 9 Condition c1 = l.newCondition(); 10 Condition c2 = l.newCondition(); 11 12 boolean flag; 13 14 public void produce() throws InterruptedException 15 { 16 l.lock(); 17 18 try{ 19 while(true) 20 { 21 while(flag) 22 c1.await(); 23 System.out.println(Thread.currentThread().getName()+"KaoYa...."+num); 24 flag = true; 25 c2.signal(); 26 } //num++; 27 28 }finally 29 { 30 l.unlock(); 31 } 32 } 33 34 public void consume() throws InterruptedException 35 { 36 l.lock(); 37 38 try{ 39 while(true) 40 { 41 while(!flag) 42 c2.await(); 43 System.out.println(Thread.currentThread().getName()+"KaoYa------"+num); 44 num++; 45 flag = false; 46 c1.signal(); 47 } 48 }finally 49 { 50 51 l.unlock(); 52 53 } 54 } 55 56 } 57 58 class Product implements Runnable 59 { 60 61 KaoYa k; 62 Product(KaoYa k) 63 { 64 65 this.k = k; 66 67 } 68 69 public void run() 70 { 71 try{ 72 k.produce(); 73 }catch(InterruptedException e) 74 { 75 76 } 77 } 78 } 79 80 81 class Consume implements Runnable 82 { 83 84 KaoYa k; 85 Consume(KaoYa k) 86 { 87 88 this.k = k; 89 90 } 91 92 public void run() 93 { 94 95 try{ 96 k.consume(); 97 }catch(InterruptedException e) 98 { 99 100 } 101 102 103 } 104 105 106 } 107 108 class LockDemo 109 { 110 111 public static void main(String[] args) { 112 113 KaoYa k = new KaoYa(); 114 Product p = new Product(k); 115 Consume c = new Consume(k); 116 117 Thread t1 = new Thread(p); 118 Thread t2 = new Thread(p); 119 Thread t3 = new Thread(c); 120 Thread t4 = new Thread(c); 121 122 123 t1.start(); 124 t2.start(); 125 t3.start(); 126 t4.start(); 127 128 } 129 130 }