生产者消费者模式的java实现(实现二)
这次采用ReentrantLock 实现生产者消费者模式,先说下ReentrantLock,通常叫做重入锁,所谓重入就是一个线程可以再次进入已经持有锁的代码块,在内部会对重入的次数进行计数,当计数为0,则释放锁。其实synchronized关键字所代表的内置锁,也是可以重入的。但是又有几点不同:
1、ReentrantLock将加锁与解锁进行分离,可以提供更细粒度的加解锁操作,内置锁基本都是全局锁(类,对象,代码块)
2、ReentrantLock提供了定时的加锁操作,这是内置锁无法做到的。
3、ReentrantLock提供了可轮询的加锁操作,即tryLock()方法,在循环中使用,可以减少线程争抢,减少系统调用和上下文切换,节省资源,这也是内置锁无法做到的。
package com.smikevon.concurrent; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ProducerConsumer_05 { public static void main(String[] args) { final Drop drop = new Drop(); new Thread(new Runnable() { public void run() { try { new Producer(drop).produce(); } catch (InterruptedException e) { e.printStackTrace(); } } },"生产者").start(); new Thread(new Runnable() { public void run() { try { new Consumer(drop).consume(); } catch (InterruptedException e) { e.printStackTrace(); } } },"消费者").start(); } static class Producer{ private Drop drop; public Producer(Drop drop) { this.drop = drop; } public void produce() throws InterruptedException{ String[] messages = { "我是", "一名程序员", "我很骄傲", "也很自豪", "爱岗敬业", "勤劳奉献", "无怨无悔", "奉献青春", "希望通过学习", "提升", "自己", "done", }; for(int i=0;i<messages.length;i++){ drop.put(messages[i]); } } } static class Consumer{ private Drop drop; public Consumer(Drop drop) { this.drop = drop; } public void consume() throws InterruptedException{ String message = ""; do{ message = drop.take(); }while(!message.equals("done")); } } static class Drop{ private Lock lock = new ReentrantLock(); //初始化条件为空 private Condition empty = lock.newCondition(); //条件为满,这里有一个就是满了 private Condition full = lock.newCondition(); private String message; private boolean isExist = false ; public void put(String message) throws InterruptedException{ while(true){ try{ lock.lock(); if(isExist){ full.await(); } this.message = message; isExist = true; empty.signal(); System.out.println(Thread.currentThread().getName()+":"+message); break; }finally{ lock.unlock(); } } } public String take() throws InterruptedException{ while(true){ try{ lock.lock(); if(!isExist){ empty.await(); } System.out.println(Thread.currentThread().getName()+":"+message); isExist = false; full.signal(); return this.message; }finally{ lock.unlock(); } } } } }
这里使用了两个condition,一个表示是否满,一个表示是否空,赋予实际意义,如果有N个线程,定义N个condition条件。这样,在调用signal() 方法时,就是明确告诉该哪个线程从waiting状态中苏醒,恢复runnable状态。减少了内置锁notifyAll()带来的线程争抢锁,争抢时带来的系统调用和上线文切换非常消耗系统资源。
第一篇和第二篇都是采用原始的锁工具实现生产者消费者模式,实际上java.util.concurrent包提供了一些方便的并发工具,这些工具能够非常方便的实现生产者消费者模式。例如Exchanger,semaphore等。