java.util.concurrent.locks 包中,规定了三个接口,来代替synchronized和Object的监视器方法,wait,notify。
即帮助我们更加灵活的控制线程的状态和同步。至于如何使用Lock请参考ReentrantLock和synchronized的区别
- Condition 代替Object的监视器方法,wait,notify。
- Lock 代替synchronized关键字
- ReadWriteLock 实现读写锁,写写互斥,读写互斥,读读不互斥。
本文使用Condition实现一个固定同步容器。
在容器已满时,生产者的线程等待,生产者生产时,即唤醒所有等待的消费者(如果有的话)。
在容器已空时,消费者的容器等待,消费者消费时,即唤醒所有等待的生产者(如果有的话)。
import java.util.Arrays; /** * 使用synchronized和Object.wait();Object.notifyAll(); * 完成的固定长度同步容器 * @author TomcatLikeYou * @param <E> */ public class SynchronizedContainer<E> { private int size = 10; private E[] array = null; private int index = 0; @SuppressWarnings("all") public SynchronizedContainer() { array = (E[]) new Object[size]; } @SuppressWarnings("all") public SynchronizedContainer(int size) { this.size = size; array = (E[]) new Object[size]; } public synchronized void add(E element) { while (index >= size) { try { System.out.println("线程:" + Thread.currentThread().getName() + " 等待消费"); this.wait(); System.out.println("线程:" + Thread.currentThread().getName() + " 被唤醒"); } catch (InterruptedException e) { e.printStackTrace(); } } array[index] = element; index++; System.out.println("线程:" + Thread.currentThread().getName() + " 将数组增加至" + this.toString()); this.notifyAll(); } public synchronized E get() { while (index <= 0) { try { System.out.println("线程:" + Thread.currentThread().getName() + " 等待生产"); this.wait(); System.out.println("线程:" + Thread.currentThread().getName() + " 被唤醒"); } catch (InterruptedException e) { e.printStackTrace(); } } index--; E element = array[index]; //下面语句这是没必要的操作,但是为了看的清楚被消费了,加上这句 array[index] = null; this.notifyAll(); System.out.println("线程:" + Thread.currentThread().getName() + " 将数组减少至" + this.toString()); return element; } @Override public String toString() { return Arrays.toString(array); } public static void main(String[] args) { SynchronizedContainer array = new SynchronizedContainer(20); for (int i = 0 ; i <2 ; i++) { new Thread(()->{ for (int j = 0 ; j < 500 ; j++) { array.get(); } },"消费者线程" + String.valueOf(i)).start(); } for (int i = 0 ; i <10 ; i++) { new Thread(()->{ for (int j = 0 ; j < 100 ; j++) { array.add(String.valueOf(j)); } },"生产者线程" + String.valueOf(i)).start(); } } }
import java.util.Arrays; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 使用Lock和Condition * 完成的固定长度同步容器 * @author TomcatLikeYou */ public class ConditionLockContainer<E> { //容器的大小 private int size = 10; //实际存储容器的数组 private E[] array = null; //index表示下一个能被添加的元素在数组中的下标 private int index = 0; //实现同步的锁 Lock lock = new ReentrantLock(); //get条件 Condition getCondition = lock.newCondition(); //put条件 Condition addCondition = lock.newCondition(); //不用加synchronized,内部使用了lock加锁 public E get() { //获取锁 lock.lock(); E element; //由于如果lock时发生异常,不会自动释放锁,故在finally中释放锁(虽然此例中不需要) try { //只要index等于0,就等待 while (index <= 0) { try { System.out.println("线程:" + Thread.currentThread().getName() + " 等待生产"); getCondition.await(); System.out.println("线程:" + Thread.currentThread().getName() + " 被唤醒"); } catch (InterruptedException e) { e.printStackTrace(); } } index--; element = array[index]; //下面语句这是没必要的操作,但是为了看的清楚被消费了,加上这句 array[index] = null; //唤醒所有等待的生产者线程 System.out.println("线程:" + Thread.currentThread().getName() + " 将数组减少至" + this.toString()); addCondition.signalAll(); } finally { //释放锁 lock.unlock(); } return element; } public void add(E element) { lock.lock(); try { while (index >= size) { try { System.out.println("线程:" + Thread.currentThread().getName() + " 等待消费"); addCondition.await(); System.out.println("线程:" + Thread.currentThread().getName() + " 被唤醒"); } catch (InterruptedException e) { e.printStackTrace(); } } array[index] = element; index++; System.out.println("线程:" + Thread.currentThread().getName() + " 将数组增加至" + this.toString()); //唤醒所有生产者线程 getCondition.signalAll(); } finally { lock.unlock(); } } @SuppressWarnings("all") public ConditionLockContainer() { array = (E[]) new Object[size]; } @SuppressWarnings("all") public ConditionLockContainer(int size) { this.size = size; array = (E[]) new Object[size]; } @Override public String toString() { return Arrays.toString(array); } public static void main(String[] args) { ConditionLockContainer array = new ConditionLockContainer(20); for (int i = 0 ; i <2 ; i++) { new Thread(()->{ for (int j = 0 ; j < 500 ; j++) { array.get(); } },"消费者线程" + String.valueOf(i)).start(); } for (int i = 0 ; i <10 ; i++) { new Thread(()->{ for (int j = 0 ; j < 100 ; j++) { array.add(String.valueOf(j)); } },"生产者线程" + String.valueOf(i)).start(); } } }
分析代码可知,使用Condition效率会更高,更灵活,唤醒的不是所有等待线程,而是指定线程。而且Condition的await可以指定多个不同方法,见API文档