java多线程-Condition

ConditionObject 监视器方法(waitnotifynotifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。

先看一个关于Condition使用的简单实例:

 1 public class ConditionTest {
 2     public static void main(String[] args) {
 3         final Lock lock = new ReentrantLock();
 4         final Condition condition = lock.newCondition();
 5         
 6         Thread thread1 = new Thread(new Runnable() {
 7             @Override
 8             public void run() {
 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(new Runnable() {
25             @Override
26             public void run() {
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@10bc3c9
2 我拿到了锁
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 class ConditionCommuniction {
 2     public static void main(String[] args) {
 3         final Business business = new Business();
 4         new Thread(new Runnable() {
 5             @Override
 6             public void run() {
 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 class Business{
19         private Lock lock = new ReentrantLock();
20         private boolean isMain = true;
21         private Condition condition = lock.newCondition();
22         public void main(int i){
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(int i){
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 class BoundedBuffer {  
 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) throws InterruptedException {  
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() throws InterruptedException {  
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        return x;  
33      } finally {  
34        lock.unlock();  
35      }  
36    }   
37  }  

  这就是多个Condition的强大之处,假设缓存队列中已经存满,那么阻塞的肯定是写线程,唤醒的肯定是读线程,相反,阻塞的肯定是读线程,唤醒的肯定是写线程,那么假设只有一个Condition会有什么效果呢,缓存队列中已经存满,这个Lock不知道唤醒的是读线程还是写线程了,如果唤醒的是读线程,皆大欢喜,如果唤醒的是写线程,那么线程刚被唤醒,又被阻塞了,这时又去唤醒,这样就浪费了很多时间。

  将上面主线程和子线程交替运行的程序进行扩展,三个线程交替运行,代码如下:

 1 public class ThreeConditionCommunication {
 2     public static void main(String[] args) {
 3         final Business business = new Business();
 4         new Thread(new Runnable() {
 5             
 6             @Override
 7             public void run() {
 8                 for (int i = 0; i < 50; i++) {
 9                     business.sub1(i);
10                 }
11             }
12         }).start();
13         new Thread(new Runnable() {
14             
15             @Override
16             public void run() {
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 class Business{
27         Lock lock = new ReentrantLock();
28         Condition main = lock.newCondition();
29         Condition sub1 = lock.newCondition();
30         Condition sub2 = lock.newCondition();
31         int runNum = 1;
32         
33         public void main(int i){
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(int i){
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(int i){
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在多线程通信的强大作用,可以大大提高程序效率。

posted @ 2016-02-22 22:59  ngulc  阅读(1666)  评论(0编辑  收藏  举报