使用ReentrantLock实现阻塞队列与交替打印
一、前言
1.ReentrantLock是可重入锁,意味着一个线程可以进入任何一个该线程已拥有的锁同步着的代码块,实现了Lock接口,通过Condition精细控制多线程休眠唤醒。
2.Lock接口
//获取锁,获取不到lock就不罢休,不可被打断,即使当前线程被中断,线程也一直阻塞,直到拿到锁, 比较无赖的做法。 void lock(); /** *获取锁,可中断,如果获取锁之前当前线程被interrupt了, *获取锁之后会抛出InterruptedException,并且停止当前线程; *优先响应中断 */ void lockInterruptibly() throws InterruptedException; //立即返回结果;尝试获得锁,如果获得锁立即返回ture,失败立即返回false boolean tryLock(); //尝试拿锁,可设置超时时间,超时返回false,即过时不候 boolean tryLock(long time, TimeUnit unit) throws InterruptedException; //释放锁 void unlock(); //返回当前线程的Condition ,可多次调用 Condition newCondition();
3.Condition接口
public interface Condition { /** *Condition线程进入阻塞状态,调用signal()或者signalAll()再次唤醒, *允许中断如果在阻塞时锁持有线程中断,会抛出异常; *重要一点是:在当前持有Lock的线程中,当外部调用会await()后,ReentrantLock就允许其他线程来抢夺锁当前锁, *注意:通过创建Condition对象来使线程wait,必须先执行lock.lock方法获得锁 */ void await() throws InterruptedException; //Condition线程进入阻塞状态,调用signal()或者signalAll()再次唤醒,不允许中断,如果在阻塞时锁持有线程中断,继续等待唤醒 void awaitUninterruptibly(); //设置阻塞时间,超时继续,超时时间单位为纳秒,其他同await();返回时间大于零,表示是被唤醒,等待时间并且可以作为等待时间期望值,小于零表示超时 long awaitNanos(long nanosTimeout) throws InterruptedException; //类似awaitNanos(long nanosTimeout);返回值:被唤醒true,超时false boolean await(long time, TimeUnit unit) throws InterruptedException; //类似await(long time, TimeUnit unit) boolean awaitUntil(Date deadline) throws InterruptedException; //唤醒指定线程 void signal(); //唤醒全部线程 void signalAll(); }
二、实现阻塞队列
1.实现
public class BlockQueue { //队列容器 private List<Integer> container = new ArrayList<>(); private Lock lock = new ReentrantLock(); //队列为空 private Condition isNull = lock.newCondition(); //队列已满 private Condition isFull = lock.newCondition(); private volatile int size; private volatile int capacity; BlockQueue(int cap){ this.capacity = cap; } public void add(int data){ try { lock.lock(); while(size>=capacity){ System.out.println("队列已满,释放锁,等待消费者消费"); try { isFull.await(); } catch (InterruptedException e) { e.printStackTrace(); } } ++size; container.add(data); isNull.signal(); } finally { lock.unlock(); } } public int take(){ try { lock.lock(); while(0==size){ System.out.println("阻塞队列空了,释放锁,等待生产者生产数据"); try { isNull.await(); } catch (InterruptedException e) { e.printStackTrace(); } } --size; int res = container.get(0); container.remove(0); isFull.signal(); return res; }finally { lock.unlock(); } } }
2.测试与运行结果
public class BlockQueueTest { public static void main(String[] args) { BlockQueue queue = new BlockQueue(5); Thread thread1 = new Thread(()->{ for(int i=0;i<100;i++){ queue.add(i); System.out.println("生产:" + i); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread thread2 = new Thread(()->{ for(;;){ System.out.println("消费者开始工作,消费:" + queue.take()); try { Thread.sleep(600); } catch (InterruptedException e) { e.printStackTrace(); } } }); thread1.start(); thread2.start(); } }
测试结果:
三、交替打印
1.实现
public class AlternateTask { public static void main(String[] args) { ReentrantLock lock = new ReentrantLock(); Condition condition1 = lock.newCondition(); Condition condition2 = lock.newCondition(); Thread thread1 = new Thread(()->{ try { lock.lock(); for (int i=1;i<100;i+=2){ System.out.println("-----thread1-----" + i ); condition1.await(); condition2.signal(); Thread.sleep(500); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } }); Thread thread2 = new Thread(()->{ try { lock.lock(); for(int i=2;i<101;i+=2){ System.out.println("-----thread2-----" + i); condition1.signal(); condition2.await(); Thread.sleep(500); } } catch (InterruptedException e){ e.getStackTrace(); } finally { lock.unlock(); } }); thread1.start(); thread2.start(); } }
2测试与运行结果
四、结语
本文是对ReentrantLock的两个经典demo做简单实现,如有纰漏请及时指正,一起学习共同进步。