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 我拿到一个信号[email protected]

可以看到,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在多线程通信的强大作用,可以大大提高程序效率。

时间: 2024-11-05 14:52:41

java多线程-Condition的相关文章

java多线程 -- Condition 控制线程通信

Api文档如此定义: Condition 将 Object 监视器方法(wait.notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set).其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用. Condition 接口描述了可能会与锁有关联的条件变量.这些变量在用法上与使用 Object.wait 访问的隐式监视器类似

Java多线程系列--“JUC锁”02之 互斥锁ReentrantLock

ReentrantLock介绍 ReentrantLock是一个可重入的互斥锁,又被称为"独占锁". 顾名思义,ReentrantLock锁在同一个时间点只能被一个线程锁持有:而可重入的意思是,ReentrantLock锁,可以被单个线程多次获取.ReentrantLock分为"公平锁"和"非公平锁".它们的区别体现在获取锁的机制上是否公平."锁"是为了保护竞争资源,防止多个线程同时操作线程而出错,ReentrantLock在

Java多线程总结之线程安全队列Queue

在Java多线程应用中,队列的使用率很高,多数生产消费模型的首选数据结构就是队列.Java提供的线程安全的Queue可以分为阻塞队列和非阻塞队列,其中阻塞队列的典型例子是BlockingQueue,非阻塞队列的典型例子是ConcurrentLinkedQueue,在实际应用中要根据实际需要选用阻塞队列或者非阻塞队列. 注:什么叫线程安全?这个首先要明确.线程安全的类 ,指的是类内共享的全局变量的访问必须保证是不受多线程形式影响的.如果由于多线程的访问(比如修改.遍历.查看)而使这些变量结构被破坏

java多线程系列6-阻塞队列

这篇文章将使用经典的生产者消费者的例子来进一步巩固java多线程通信,介绍使用阻塞队列来简化程序 下面是一个经典的生产者消费者的例子: 假设使用缓冲区存储整数,缓冲区的大小是受限制的.缓冲区提供write(int)方法将一个整数添加到缓冲区,还体统read()方法从缓冲区中读取并删除一个整数.为了同步操作,使用具有两个条件的锁,notEmpty(缓冲区非空)和notFull(缓冲区未满).当任务相缓冲区添加一个int时,如果缓冲区是满的,那么任务将等待notFull状态,当任务从缓冲区总删除一个

Java多线程面试15道

Java 线程面试问题 在任何Java面试当中多线程和并发方面的问题都是必不可少的一部分.如果你想获得任何股票投资银行的前台资讯职位,那么你应该准备很多关于多线程的问题.在投资银行业务中多线程和并发是一个非常受欢迎的话题,特别是电子交易发展方面相关的.他们会问面试者很多令人混淆的Java线程问题.面试官只是想确信面试者有足够的Java线程与并发方面的知识,因为候选人中有很多只浮于表面.用于直接面向市场交易的高容量和低延时的电子交易系统在本质上是并发的.下面这些是我在不同时间不同地点喜欢问的Jav

【转】 Java 多线程之一

转自   Java 多线程 并发编程 一.多线程 1.操作系统有两个容易混淆的概念,进程和线程. 进程:一个计算机程序的运行实例,包含了需要执行的指令:有自己的独立地址空间,包含程序内容和数据:不同进程的地址空间是互相隔离的:进程拥有各种资源和状态信息,包括打开的文件.子进程和信号处理. 线程:表示程序的执行流程,是CPU调度执行的基本单位:线程有自己的程序计数器.寄存器.堆栈和帧.同一进程中的线程共用相同的地址空间,同时共享进进程锁拥有的内存和其他资源. 2.Java标准库提供了进程和线程相关

java多线程小结,及解决应用挂死的问题

这两天为了定位JBOSS老是挂死的问题,学习了一下JAVA多线程方面的知识,在此总结一下 1.在Java程序中,JVM负责线程的调度.线程调度是指按照特定的机制为多个线程分配CPU的使用权. 调度的模式有两种:分时调度和抢占式调度.分时调度是所有线程轮流获得CPU使用权,并平均分配每个线程占用CPU的时间:抢占式调度是根据线程的优先级别来获取CPU的使用权.JVM的线程调度模式采用了抢占式模式. 2.Thread类实际上也是实现了Runnable接口的类. 在启动的多线程的时候,需要先通过Thr

java 多线程并发问题总结

java 多线程并发主要通过关键字synchronized实现 Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 一.当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行.另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块. 二.然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该ob

Java多线程技术学习笔记(二)

目录: 线程间的通信示例 等待唤醒机制 等待唤醒机制的优化 线程间通信经典问题:多生产者多消费者问题 多生产多消费问题的解决 JDK1.5之后的新加锁方式 多生产多消费问题的新解决办法 sleep和wait的区别 停止线程的方式 守护线程 线程的其他知识点 一.线程间的通信示例 返目录回 多个线程在处理同一资源,任务却不同. 假设有一堆货物,有一辆车把这批货物往仓库里面运,另外一辆车把前一辆车运进仓库的货物往外面运.这里货物就是同一资源,但是两辆车的任务却不同,一个是往里运,一个是往外运. 下面