分享JUC中的Condition的比较少见,我见了大部分文章都是讲其中的一个例子BoundedBuffer。今天先从Condition接口的几个方法提及,而后在把BoundedBuffer搞死锁了。来看看Condition在使用的时候须要注意什么。java
上源代码:(字数限时,原谅我把注释都去掉了)数组
public interface Condition { void await() throws InterruptedException; void awaitUninterruptibly(); long awaitNanos(long nanosTimeout) throws InterruptedException; boolean await(long time, TimeUnit unit) throws InterruptedException; boolean awaitUntil(Date deadline) throws InterruptedException; void signal(); void signalAll(); }
Condtion接口主要是用来描述一个锁的几个状态。具体建立方法以下
ide
Condition notFull = lock.newCondition();
其实Condition的功能有点相似Object当中的wait和notify方法。可是稍作了增强。提供了多种wait的策略。另外用Condition的最大好处就是,一个锁是能够拥有多个状态的。若是用Object的wait和notify只能有一个。
测试
具体看一下如下几个wait方法:
this
void await() throws InterruptedException; void awaitUninterruptibly(); long awaitNanos(long nanosTimeout) throws InterruptedException; boolean await(long time, TimeUnit unit) throws InterruptedException; boolean awaitUntil(Date deadline) throws InterruptedException;
await()spa
解读:会将当前线程设置成等待状态,一直到有signal方法的触发或者Interrupt的发生才会恢复状态。固然interrupt了就直接抛异常了,不会继续往下走线程
异常:InterruptedExceptioncode
awaitUninterruptibly()接口
解读:会将当前线程设置成等待状态,一直到有signal方法的触发。不会被Interrupt阻断。get
long awaitNanos(long nanosTimeout)
参数:最大的等待的时间
返回:实际的剩余时间的估算(nanosTimeout - 实际等待时间)正值能够用于后续的继续等待,例如延迟加载这样的场景,可让程序继续等待剩下的时间,完成计时。若是为0或者负数时,表示没有剩余时间了。
解读:会将当前线程设置成等待状态一直到设定的最大等待时间。当遇到singal或者Interrupt时才会恢复。
异常:InterruptedException
boolean await(long time, TimeUnit unit) 和 boolean awaitUntil(Date deadline)
参数:具体时间一个是间断的时间,另外一个是具体的时刻。但实质是同样的。
解读:具体等待一段时间或一个到一个时间点。若是遇到singal则返回True,不然返回false.
异常:InterruptedException
void signal(); void signalAll();
void signal()
解读:唤醒一个等待的线程,若是全部的线程都在等待此条件,则选择其中的一个唤醒。在从 await 返回以前,该线程必须从新获取锁。
void signalAll()
解读:唤醒全部线程,若是全部的线程都在等待此条件,则选择其中的一个唤醒。在从 await 返回以前,该线程必须从新获取锁。
public class BoundedBuffer { final Lock lock = new ReentrantLock(); final Condition notFull = lock.newCondition(); final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[2]; int putptr, takeptr, count; public void put(Object x) throws InterruptedException { String threadName = Thread.currentThread().getName(); System.out.println(threadName+" is waiting for lock"); lock.lock(); System.out.println(threadName+" got lock"); try { while (count == items.length){ System.out.println(threadName+" is waiting for notFull condition"); notFull.await(); System.out.println(threadName+" left notFull condition"); } items[putptr] = x; if (++putptr == items.length){ putptr = 0; } ++count; notEmpty.signal(); } finally { lock.unlock(); } } public Object take() throws InterruptedException { String threadName = Thread.currentThread().getName(); System.out.println(threadName+" is waiting for lock"); lock.lock(); System.out.println(threadName+" got lock"); try { while (count == 0){ System.out.println(threadName+" is waiting for notEmpty condition"); notEmpty.await(); System.out.println(threadName+" left notEmpty condition"); } Object x = items[takeptr]; if (++takeptr == items.length){ takeptr = 0; } --count; notFull.signal(); return x; } finally { lock.unlock(); } } }
问得最多的一个问题:这里的await有必要吗?
while (count == 0){ System.out.println(threadName+" is waiting for notEmpty condition"); notEmpty.await(); System.out.println(threadName+" left notEmpty condition"); }
回答:有必要,当count等于0的时候.若是没有await释放锁,那么其余线程会认为lock一直被占有,将没法得到锁。
为了验证这一点,写了一个测试方法:
public class PutTask implements Runnable { BoundedBuffer bb = new BoundedBuffer(); int count = 0; public PutTask(BoundedBuffer bb){ this.bb = bb; this.count = 0; } @Override public void run() { try { String threadName = Thread.currentThread().getName(); bb.put(count); System.out.println(threadName+" put "+count); count++; } catch (InterruptedException e) { e.printStackTrace(); } } } public class TakeTask implements Runnable { BoundedBuffer bb = new BoundedBuffer(); public TakeTask(BoundedBuffer bb){ this.bb = bb; } @Override public void run() { try { System.out.println(Thread.currentThread().getName()+" take "+bb.take()); } catch (InterruptedException e) { e.printStackTrace(); } } } public class ConditionTest { public static void main(String[] args) throws InterruptedException{ BoundedBuffer bb = new BoundedBuffer(); PutTask putTask = new PutTask(bb); TakeTask takeTask1 = new TakeTask(bb); TakeTask takeTask2 = new TakeTask(bb); final ScheduledExecutorService service = Executors.newScheduledThreadPool(3); //service.scheduleAtFixedRate(putTask, 0, 1, TimeUnit.SECONDS); service.scheduleAtFixedRate(takeTask1, 0, 1, TimeUnit.SECONDS); service.scheduleAtFixedRate(takeTask2, 0, 1, TimeUnit.SECONDS); Thread.currentThread().sleep(3000l); Thread t = new Thread(putTask); t.start(); } }
将notEmpty.await()注释掉,执行的结果为
pool-1-thread-2 is waiting for lock pool-1-thread-2 got lock pool-1-thread-1 is waiting for lock Thread-0 is waiting for lock
能够看到除了pool-1-thread-2得到了锁,其余线程都在等待锁。但这个时候pool-1-thread-2被while(count==0)锁死,没法跳出。程序进入死锁。
恢复notEmpty.await()的注释,执行的结果为
pool-1-thread-2 is waiting for lock pool-1-thread-2 got lock pool-1-thread-2 is waiting for notEmpty condition pool-1-thread-1 is waiting for lock pool-1-thread-1 got lock pool-1-thread-1 is waiting for notEmpty condition Thread-0 is waiting for lock Thread-0 got lock Thread-0 put 0 pool-1-thread-2 left notEmpty condition pool-1-thread-2 take 0 pool-1-thread-2 is waiting for lock pool-1-thread-2 got lock pool-1-thread-2 is waiting for notEmpty condition
问题:
while (count == 0){ System.out.println(threadName+" is waiting for notEmpty condition"); notEmpty.await(); System.out.println(threadName+" left notEmpty condition"); }
若是将while改为if能够吗?
回答:不能够,
notEmpty.signal();
若是将notEmpty.signal()改为notEmpty.signalAll()。而后按照上面的方法再来试一次,先放两个线程去take,而后一个线程去put,而后同时唤醒两个线程。数组会越界。必须从新等待一次notEmpty.
最后一个问题
public class ConditionTest { public static void main(String[] args) throws InterruptedException{ BoundedBuffer bb = new BoundedBuffer(); PutTask putTask = new PutTask(bb); TakeTask takeTask1 = new TakeTask(bb); TakeTask takeTask2 = new TakeTask(bb); final ScheduledExecutorService service = Executors.newScheduledThreadPool(2); service.scheduleAtFixedRate(putTask, 0, 1, TimeUnit.SECONDS); service.scheduleAtFixedRate(takeTask1, 0, 1, TimeUnit.SECONDS); service.scheduleAtFixedRate(takeTask2, 0, 1, TimeUnit.SECONDS); } }
若是这样执行,结果会怎么样?在两个线程中跑3个任务。
结果就是死锁了:
pool-1-thread-1 is waiting for lock pool-1-thread-1 got lock pool-1-thread-1 put 0 pool-1-thread-1 is waiting for lock pool-1-thread-1 got lock pool-1-thread-1 take 0 pool-1-thread-1 is waiting for lock pool-1-thread-1 got lock pool-1-thread-1 is waiting for notEmpty condition pool-1-thread-2 is waiting for lock pool-1-thread-2 got lock pool-1-thread-2 put 1 pool-1-thread-1 left notEmpty condition pool-1-thread-1 take 1 pool-1-thread-1 is waiting for lock pool-1-thread-1 got lock pool-1-thread-1 is waiting for notEmpty condition pool-1-thread-2 is waiting for lock pool-1-thread-2 got lock pool-1-thread-2 is waiting for notEmpty condition