在一个锁中使用多个条件(Using multiple conditions in a Lock)
一个Lock可能关联着一个或者多个条件,这些条件表现在Condition接口。这些条件(conditions)的目的是允许多个线程控制一个锁对象并且检查一个条件是真还是假,当一个条件为false时,那么线程将会被挂起,直到其他线程唤醒它;Condition接口提供了挂起一个线程和唤醒一个线程的机制;
在之前的生产者消费者例子中,使用了Lock来同步临界区,生产者和消费者都只有一个线程;在下面的例子中,生产者和消费者都将有多个线程,当缓冲区满时,所有的生产者将会被挂起,消费者负责唤醒生产者;当缓冲区空时,消费者将被挂起,生茶者负责唤醒消费者;
动手实现
(1)创建一个大小固定的队列作为生产者和消费者的缓冲区;
public class MyQueue(2)创建消费者{ private ReentrantLock lock=new ReentrantLock(); private Condition pullConditon=lock.newCondition(); private Condition pushCondition=lock.newCondition(); private int maxSize; private LinkedList list=new LinkedList<>(); public MyQueue(int size) { maxSize=size; } public void push(T t){ lock.lock(); try { while (list.size()== maxSize) { // Current push thread release lock and sleep. pushCondition.await(); } list.push(t); System.out.printf("%s Push Size %d\n", Thread.currentThread().getName(), list.size()); // Week up all pull thread pullConditon.signalAll(); } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } } public T pull(){ T t=null; lock.lock(); try { while (list.size() == 0) { // Current poll thread release lock and sleep. pullConditon.await(); } t=list.poll(); System.out.printf("%s Pull Size %d\n", Thread.currentThread().getName(), list.size()); //Week up all push threads pushCondition.signalAll(); } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } return t; }}
public class Consumer implements Runnable{ private MyQueue(3)创建生产者myQueue; public Consumer(MyQueue myQueue) { this.myQueue = myQueue; } @Override public void run() { for (int i = 0; i < 50; i++) { myQueue.pull(); try { Random random=new Random(); Thread.sleep(random.nextInt(500)); } catch (InterruptedException e) { e.printStackTrace(); } } }}
public class Producer implements Runnable{ private MyQueue(4)Main,创建3个消费者,3个生产者myQueue; public Producer(MyQueue myQueue) { this.myQueue = myQueue; } @Override public void run() { for (int i = 0; i < 50; i++) { myQueue.push(i); try { Random random=new Random(); Thread.sleep(random.nextInt(500)); } catch (InterruptedException e) { e.printStackTrace(); } } }}
public class Main { public static void main(String[] args) { MyQueue由于生产者和消费者一样多,生产者生产的和消费者消费的刚好,所以最终程序会正常结束;myQueue = new MyQueue<>(100); Thread[] producer = new Thread[3]; Thread[] consumer = new Thread[3]; for (int i=0;i
要点
在该例子中,当一个生产者线程占用了锁对象,那么其他的生产者线程和消费者线程都将被挂起,直到该线程释放了锁对象;当队列满时,生产者将执行pushConditon接口的await()方法,执行该方法,该线程将会被挂起,并且同时释放掉锁对象,从而允许其它线程执行;到消费者执行pushConditon的signalAll()方法时(注意这里是signalAll()而不是notifyAll()),将会唤醒被所有被pushCondtion挂起的线程,即唤醒生产者;从这个例子生越来越感觉Java线程机制的强大和灵活;