Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。
先看一个关于Condition使用的简单实例:
1 public classConditionTest {2 public static voidmain(String[] args) {3 final Lock lock = newReentrantLock();4 final Condition condition =lock.newCondition();5
6 Thread thread1 = new Thread(newRunnable() {7 @Override8 public voidrun() {9 try{10 lock.lock();11 System.out.println("我需要等一个信号"+this);12 condition.await();13 System.out.println("我拿到一个信号"+this);14 } catch(Exception e) {15 //TODO: handle exception
16 } finally{17 lock.unlock();18 }19
20
21 }22 }, "thread1");23 thread1.start();24 Thread thread2 = new Thread(newRunnable() {25 @Override26 public voidrun() {27 try{28 lock.lock();29 System.out.println("我拿到了锁");30 Thread.sleep(500);31 System.out.println("我发出一个信号");32 condition.signal();33 } catch(Exception e) {34 //TODO: handle exception
35 } finally{36 lock.unlock();37 }38
39
40 }41 }, "thread2");42 thread2.start();43 }44 }
运行结果:
1 我需要等一个信号com.luchao.traditionalthread.ConditionTest$1@10bc3c92 我拿到了锁3 我发出一个信号4 我拿到一个信号com.luchao.traditionalthread.ConditionTest$1@10bc3c9
View Code
可以看到,Condition的执行方式,是当在线程1中调用await方法后,线程1将释放锁,并且将自己沉睡,等待唤醒,线程2获取到锁后,开始做事,完毕后,调用Condition的signal方法,唤醒线程1,线程1恢复执行。
以上说明Condition是一个多线程间协调通信的工具类,使得某个,或者某些线程一起等待某个条件(Condition),只有当该条件具备( signal 或者 signalAll方法被带调用)时 ,这些等待线程才会被唤醒,从而重新争夺锁。
Condition与传统线程通信有些类似,它的使用更广,可以将多个线程进行通信,以完成更加复杂的通信。
用Condition替换传统线程通信,在前面的传统有一个子线程和主线程交替运行50次的实例,使用Condition也可以完成。
代码如下:
1 public classConditionCommuniction {2 public static voidmain(String[] args) {3 final Business business = newBusiness();4 new Thread(newRunnable() {5 @Override6 public voidrun() {7 for (int i = 0; i < 50; i++) {8 business.sub(i);9 }10 }11 }).start();12 for (int i = 0; i < 50; i++) {13 business.main(i);14 }15 }16
17
18 static classBusiness{19 private Lock lock = newReentrantLock();20 private boolean isMain = true;21 private Condition condition =lock.newCondition();22 public void main(inti){23 lock.lock();24 try{25 while(!isMain){26 condition.await();27 }28 for (int j = 0; j < 100; j++) {29 System.out.println("main is looping :" + j +" in " +i);30 }31 isMain = false;32 condition.signal();33 } catch(Exception e) {34 //TODO: handle exception
35 } finally{36 lock.unlock();37 }38 }39 public void sub(inti){40 lock.lock();41 try{42 while(isMain){43 condition.await();44 }45 for (int j = 0; j < 10; j++) {46 System.out.println("sub is looping :" + j +" in " +i);47 }48 isMain = true;49 condition.signal();50 } catch(Exception e) {51 //TODO: handle exception
52 } finally{53 lock.unlock();54 }55 }56 }57 }
在Condition中,用await()替换wait(),用signal()替换notify(),用signalAll()替换notifyAll(),传统线程的通信方式,Condition都可以实现,这里注意,Condition是被绑定到Lock上的,要创建一个Lock的Condition必须用newCondition()方法。
这样看来,Condition和传统的线程通信没什么区别,Condition的强大之处在于它可以为多个线程间建立不同的Condition,下面引入API中的一段代码,加以说明。
1 classBoundedBuffer {2 final Lock lock = new ReentrantLock();//锁对象
3 final Condition notFull = lock.newCondition();//写线程条件
4 final Condition notEmpty = lock.newCondition();//读线程条件
5
6 final Object[] items = new Object[100];//缓存队列
7 int putptr/*写索引*/, takeptr/*读索引*/, count/*队列中存在的数据个数*/;8
9 public void put(Object x) throwsInterruptedException {10 lock.lock();11 try{12 while (count == items.length)//如果队列满了
13 notFull.await();//阻塞写线程
14 items[putptr] = x;//赋值
15 if (++putptr == items.length) putptr = 0;//如果写索引写到队列的最后一个位置了,那么置为0
16 ++count;//个数++
17 notEmpty.signal();//唤醒读线程
18 } finally{19 lock.unlock();20 }21 }22
23 public Object take() throwsInterruptedException {24 lock.lock();25 try{26 while (count == 0)//如果队列为空
27 notEmpty.await();//阻塞读线程
28 Object x = items[takeptr];//取值
29 if (++takeptr == items.length) takeptr = 0;//如果读索引读到队列的最后一个位置了,那么置为0
30 --count;//个数--
31 notFull.signal();//唤醒写线程
32 returnx;33 } finally{34 lock.unlock();35 }36 }37 }
这就是多个Condition的强大之处,假设缓存队列中已经存满,那么阻塞的肯定是写线程,唤醒的肯定是读线程,相反,阻塞的肯定是读线程,唤醒的肯定是写线程,那么假设只有一个Condition会有什么效果呢,缓存队列中已经存满,这个Lock不知道唤醒的是读线程还是写线程了,如果唤醒的是读线程,皆大欢喜,如果唤醒的是写线程,那么线程刚被唤醒,又被阻塞了,这时又去唤醒,这样就浪费了很多时间。
将上面主线程和子线程交替运行的程序进行扩展,三个线程交替运行,代码如下:
1 public classThreeConditionCommunication {2 public static voidmain(String[] args) {3 final Business business = newBusiness();4 new Thread(newRunnable() {5
6 @Override7 public voidrun() {8 for (int i = 0; i < 50; i++) {9 business.sub1(i);10 }11 }12 }).start();13 new Thread(newRunnable() {14
15 @Override16 public voidrun() {17 for (int i = 0; i < 50; i++) {18 business.sub2(i);19 }20 }21 }).start();22 for (int i = 0; i < 50; i++) {23 business.main(i);24 }25 }26 static classBusiness{27 Lock lock = newReentrantLock();28 Condition main =lock.newCondition();29 Condition sub1 =lock.newCondition();30 Condition sub2 =lock.newCondition();31 int runNum = 1;32
33 public void main(inti){34 lock.lock();35 try{36 while(runNum!=1){37 main.await();//主线程等待
38 }39 for (int j = 0; j < 100; j++) {40 System.out.println("main is looping of "+j+" in "+i);41 }42 runNum = 2;43 sub1.signal();//唤醒子线程1
44 } catch(Exception e) {45 //TODO: handle exception
46 } finally{47 lock.unlock();48 }49 }50 public void sub1(inti){51 lock.lock();52 try{53 while(runNum!=2){54 sub1.await();//子线程1等待
55 }56 for (int j = 0; j < 10; j++) {57 System.out.println("sub1 is looping of "+j+" in "+i);58 }59 runNum = 3;60 sub2.signal();//唤醒子线程2
61 } catch(Exception e) {62 //TODO: handle exception
63 } finally{64 lock.unlock();65 }66 }67 public void sub2(inti){68 lock.lock();69 try{70 while(runNum!=3){71 sub2.await();//子线程2等待
72 }73 for (int j = 0; j < 20; j++) {74 System.out.println("sub2 is looping of "+j+" in "+i);75 }76 runNum = 1;77 main.signal();//唤醒主线程
78 } catch(Exception e) {79 //TODO: handle exception
80 } finally{81 lock.unlock();82 }83 }84 }85 }
由此可见,Condition在多线程通信的强大作用,可以大大提高程序效率。