13-Java5条件阻塞Condition的应用
class BoundedBuffer { final Lock lock = new ReentrantLock(); final Condition notFull = lock.newCondition(); final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[100]; int putptr, takeptr, count; public void put(Object x) throws InterruptedException { lock.lock(); try { while (count == items.length) notFull.await(); items[putptr] = x; if (++putptr == items.length) putptr = 0; ++count; notEmpty.signal(); } finally { lock.unlock(); } } public Object take() throws InterruptedException { lock.lock(); try { while (count == 0) notEmpty.await(); Object x = items[takeptr]; if (++takeptr == items.length) takeptr = 0; --count; notFull.signal(); return x; } finally { lock.unlock(); } } }
实现注意事项
在等待 Condition
时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为 Condition
应该总是在一个循环中被等待,并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。
三种形式的条件等待(可中断、不可中断和超时)在一些平台上的实现以及它们的性能特征可能会有所不同。尤其是它可能很难提供这些特性和维护特定语义,比如排序保证。更进一步地说,中断线程实际挂起的能力在所有平台上并不是总是可行的。
因此,并不要求某个实现为所有三种形式的等待定义完全相同的保证或语义,也不要求其支持中断线程的实际挂起。
要求实现清楚地记录每个等待方法提供的语义和保证,在某个实现不支持中断线程的挂起时,它必须遵从此接口中定义的中断语义。
由于中断通常意味着取消,而又通常很少进行中断检查,因此实现可以先于普通方法的返回来对中断进行响应。即使出现在另一个操作后的中断可能会释放线程锁时也是如此。实现应记录此行为。
把this.wait()和this.notify()换成Condiction,思想是一样的
package cn.itcast.demo.thread; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ConditionCommunication { // main 方法本身就是一个线程【当作是主线程,main 方法内的线程就是子线程】 public static void main(String[] args) { final Business business = new Business(); // 子线程 new Thread(new Runnable() { @Override public void run() { for (int i=1; i<=50; i++) { business.sub(i); } } }).start(); // 主线程[main方法本身就是一个线程] for (int i=1; i<=50; i++) { business.main(i); } } // 业务类 static class Business { // 上锁 Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); // 主线程和子线程交互执行的标识变量 private boolean beShouldSub = true; // 子线程 public void sub(int i) { lock.lock(); // 符合条件,子线程执行 try { // 不符合条件,让给主线程执行 while (!beShouldSub) { // 这里用if或while都可以,但是while更好,因为不需要main方法判断就已经执行,下面一样 try { //this.wait(); condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } } for (int j=1; j<=10; j++) { System.out.println("sub thread sequece of " + j + ", loop thread sequece of " + i); } // 执行完之后,切换状态 beShouldSub = false; // 唤醒主线程,去执行 //this.notify(); condition.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } // 主线程 public synchronized void main(int i) { lock.lock(); // 符合条件,主线程执行 try { // 不符合条件,子线程执行 while (beShouldSub) { try { //this.wait(); condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } } for (int j=1; j<=100; j++) { System.out.println("main thread sequece of " + j + ", loop thread sequece of " + i); } // 执行完之后,切换状态 beShouldSub = true; // 唤醒子线程,去执行 //this.notify(); condition.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } } }
打印结果:主线程和子线程交替执行
范例:主线程执行10次,子线程1执行15次,子线程2执行20次,这是一个周期,这样的周期循环50次【用Condition实现】
package cn.itcast.demo.thread; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ThreeConditionCommunication { // main 方法本身就是一个线程【当作是主线程,main 方法内的线程就是子线程】 public static void main(String[] args) { final Business business = new Business(); // 子线程1 new Thread(new Runnable() { @Override public void run() { for (int i=1; i<=50; i++) { business.sub1(i); } } }).start(); // 子线程2 new Thread(new Runnable() { @Override public void run() { for (int i=1; i<=50; i++) { business.sub2(i); } } }).start(); // 主线程[main方法本身就是一个线程] for (int i=1; i<=50; i++) { business.main(i); } } // 业务类 static class Business { // 上锁 Lock lock = new ReentrantLock(); Condition main_condition = lock.newCondition(); Condition sub1_condition = lock.newCondition(); Condition sub2_condition = lock.newCondition(); // 主线程和子线程交互执行的标识变量 private int shouldSub = 0; // 开始默认是主线程执行,1表示主线程,2表示子线程1,3表示子线程2 // 子线程1 public void sub1(int i) { lock.lock(); // 符合条件,子线程执行 try { // 不符合条件(即不是子线程1执行),让给主线程执行 while (shouldSub != 1) { // 这里用if或while都可以,但是while更好,因为不需要main方法判断就已经执行,下面一样 try { //this.wait(); sub1_condition.await(); // 子线程1等待 } catch (InterruptedException e) { e.printStackTrace(); } } for (int j=1; j<=15; j++) { System.out.println("sub1 thread sequece of " + j + ", loop thread sequece of " + i); } // 执行完之后,切换状态 shouldSub = 2; // 唤醒子线程2去执行 // 唤醒主线程,去执行 //this.notify(); sub2_condition.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } // 子线程2 public void sub2(int i) { lock.lock(); // 符合条件,子线程执行 try { // 不符合条件(即不是子线程2执行),让给主线程执行 while (shouldSub != 2) { // 这里用if或while都可以,但是while更好,因为不需要main方法判断就已经执行,下面一样 try { //this.wait(); sub2_condition.await(); // 子线程2等待 } catch (InterruptedException e) { e.printStackTrace(); } } for (int j=1; j<=20; j++) { System.out.println("sub2 thread sequece of " + j + ", loop thread sequece of " + i); } // 执行完之后,切换状态 shouldSub = 0; // 唤醒主线程去执行 // 唤醒主线程,去执行 //this.notify(); main_condition.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } // 主线程 public synchronized void main(int i) { lock.lock(); // 符合条件,主线程执行 try { // 不符合条件(不是主线程执行),子线程执行 while (shouldSub != 0) { try { //this.wait(); main_condition.await(); // 主线程等待 } catch (InterruptedException e) { e.printStackTrace(); } } for (int j=1; j<=10; j++) { System.out.println("main thread sequece of " + j + ", loop thread sequece of " + i); } // 执行完之后,切换状态 shouldSub = 1; // 唤醒子线程1去执行 // 唤醒子线程,去执行 //this.notify(); sub1_condition.signal(); // 子线程1去执行 } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } } }
打印结果:
main thread sequece of 1, loop thread sequece of 1 main thread sequece of 2, loop thread sequece of 1 main thread sequece of 3, loop thread sequece of 1 main thread sequece of 4, loop thread sequece of 1 main thread sequece of 5, loop thread sequece of 1 main thread sequece of 6, loop thread sequece of 1 main thread sequece of 7, loop thread sequece of 1 main thread sequece of 8, loop thread sequece of 1 main thread sequece of 9, loop thread sequece of 1 main thread sequece of 10, loop thread sequece of 1 sub1 thread sequece of 1, loop thread sequece of 1 sub1 thread sequece of 2, loop thread sequece of 1 sub1 thread sequece of 3, loop thread sequece of 1 sub1 thread sequece of 4, loop thread sequece of 1 sub1 thread sequece of 5, loop thread sequece of 1 sub1 thread sequece of 6, loop thread sequece of 1 sub1 thread sequece of 7, loop thread sequece of 1 sub1 thread sequece of 8, loop thread sequece of 1 sub1 thread sequece of 9, loop thread sequece of 1 sub1 thread sequece of 10, loop thread sequece of 1 sub1 thread sequece of 11, loop thread sequece of 1 sub1 thread sequece of 12, loop thread sequece of 1 sub1 thread sequece of 13, loop thread sequece of 1 sub1 thread sequece of 14, loop thread sequece of 1 sub1 thread sequece of 15, loop thread sequece of 1 sub2 thread sequece of 1, loop thread sequece of 1 sub2 thread sequece of 2, loop thread sequece of 1 sub2 thread sequece of 3, loop thread sequece of 1 sub2 thread sequece of 4, loop thread sequece of 1 sub2 thread sequece of 5, loop thread sequece of 1 sub2 thread sequece of 6, loop thread sequece of 1 sub2 thread sequece of 7, loop thread sequece of 1 sub2 thread sequece of 8, loop thread sequece of 1 sub2 thread sequece of 9, loop thread sequece of 1 sub2 thread sequece of 10, loop thread sequece of 1 sub2 thread sequece of 11, loop thread sequece of 1 sub2 thread sequece of 12, loop thread sequece of 1 sub2 thread sequece of 13, loop thread sequece of 1 sub2 thread sequece of 14, loop thread sequece of 1 sub2 thread sequece of 15, loop thread sequece of 1 sub2 thread sequece of 16, loop thread sequece of 1 sub2 thread sequece of 17, loop thread sequece of 1 sub2 thread sequece of 18, loop thread sequece of 1 sub2 thread sequece of 19, loop thread sequece of 1 sub2 thread sequece of 20, loop thread sequece of 1 ........................... ............................ 以下还要循环49次