生产者消费者模型
有多个生产者线程和多个消费者线程, 中间有一个缓存区,缓存区满了之后,生产者线程阻塞,缓冲区为空时,消费者线程阻塞。
通过synchronized和wait/ notify来实现
1)更加深刻理解了: 多个线程在并发的时候,通过synchronized加锁,那么在同一时刻只有一个线程来执行同步方法或者同步块中的代码(最多只有一个!!!)
2)wait 与 notify的使用一定要放在synchronized方法或者同步快中,而且 调用wait / notify的对象与synchronized锁的对象是同一个,否则报,IllegalMonitorStateException异常(也是 RuntimeException 运行时异常的子类)
3)我还曾试图将 缓存区满时 和 缓存区空时,分别用不同的锁对象,发现根本就没法实现(或许这就是ReentrantLock的一个意义把)
4)在同步代码块中,if语句不好使,要用 while语句来进行判断。也就是要将wait 或者 await放到while 语句中,而不是if语句中。
【
count++ 和 count-- 在底层包含三个操作, 1)获取count 的值 2)将count的值+1或者-1 3)将结果赋给count
所以在并发的情况下,对count++或者count-- 要进行上锁,让其成为临界资源! 或者用 AtomicInteger 原子操作类
】
public class Test { public static void main(String[] args) { Demo demo=new Demo(); // 生产者线程 for(int i=0;i<5;i++){ new Thread(demo.new ProducerThread(),"producer"+i).start(); } // 消费者线程 for(int i=0;i<5;i++){ new Thread(demo.new ConsumerThread(),"consumer"+i).start(); } try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } } } class Demo{ int count=5; // 缓存区容量为5 String full="a",empty="b"; // 生产 void produce() { // 同步加在这里吗? synchronized (this) { // 如果缓存满了 生产者阻塞 // if (count == 5) { while(count==5){ // 如果用if ,那么唤醒后,获取到锁后,就会直接往下执行,不会进行判断了,显然不合适,会造成超过缓存区的容量 try { // full.wait(); this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } count++; System.out.println(Thread.currentThread().getName()+":"+count); this.notify(); } } //消费 void consume(){ // 如果缓存空了 消费者阻塞 // synchronized (empty){ synchronized (this){ // if(count==0){ while (count==0){ try { // empty.wait(); this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } count--; // full.notify(); System.out.println(Thread.currentThread().getName()+":"+count); this.notify(); } } class ProducerThread implements Runnable{ @Override public void run() { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } while(true){ produce(); } } } class ConsumerThread implements Runnable{ @Override public void run() { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } while(true){ consume(); } } } }
通过ReentrantLock和 Condition来实现
ReentrantLock的不同之处就是加了两个不同的锁,提高了并发效率。
错了,也是得用同一把锁!!!! 用不同的锁,也会报 IllegalMonitorStateException 异常,就是加的锁和使用的 Condition的实例必须是对应的。但是可以用不同的Condition, 当然这些Condition是通过同一把锁来得到的。
【
思考一下:ReentrantLock是怎么加锁的? 调用 lock.lock() 方法,底层通过AQS来判断资源是否被占有,没有被占有就继续向下执行,也就是执行try中的代码,如果被占有,就挂起当前线程。(调用LockSupport,park )
】
public class Test { public static void main(String[] args) { Demo demo=new Demo(); // 生产者线程 for(int i=0;i<5;i++){ new Thread(demo.new ProducerThread(),"producer"+i).start(); } // 消费者线程 for(int i=0;i<5;i++){ new Thread(demo.new ConsumerThread(),"consumer"+i).start(); } try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } } } class Demo{ int count=5; // 缓存区容量为5 ReentrantLock lock1=new ReentrantLock(); // ReentrantLock lock2=new ReentrantLock(); Condition condition1=lock1.newCondition(); Condition condition2=lock1.newCondition(); String full="a",empty="b"; // 生产 void produce() { // 同步加在这里吗? lock1.lock(); try { while(count == 5) { try { condition1.await(); // 会释放资源吗? 会,不用while 结果也是会出错!!!! } catch (InterruptedException e) { e.printStackTrace(); } } count++; condition2.signal(); System.out.println(Thread.currentThread().getName() + ":" + count); }finally { lock1.unlock(); } } //消费 void consume(){ lock1.lock(); try { while(count==0) { try { condition2.await(); } catch (InterruptedException e) { e.printStackTrace(); } } count--; condition1.signal(); System.out.println(Thread.currentThread().getName()+":"+count); }finally { lock1.unlock(); } } class ProducerThread implements Runnable{ @Override public void run() { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } while(true){ produce(); } } } class ConsumerThread implements Runnable{ @Override public void run() { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } while(true){ consume(); } } } }
通过阻塞队列来实现
浙公网安备 33010602011771号