【Java多线程与并发库】11.java5条件阻塞Condition的应用
来源:互联网 发布:网络女神小茉莉 编辑:程序博客网 时间:2024/06/05 13:30
Condition
(1)概述
Condition是Java有关锁的包下面的一个类,锁我们只能实现互斥,不能实现通知。而Condition就是解决这个问题,Condition的功能类似在传统线程技术中的Object.wait和Object.notify的功能。
(2)实例
记得我们之前写过的一个例子,一道面试题:
子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次,
接着再回到主线程又循环100次,如此循环50次。
代码是这样的:
我们这里要对它进行该写,使用Java5的并发库中的“锁(lock)”和“条件(Condition)”,
来代替原来的“synchronized”和“wait/notify”。
改写后的部分代码:
运行效果:
在等待Condition时,允许发生“虚假唤醒”,这通常作为对基础平台语义上的让步。对于大多数应用程序,这带来的实际影响很小,因为Condition应该总是在一个循环中被等待,并测试正在被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序的程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。
为了防止“虚假唤醒”,所以我们要把上面的
(3)优势
既然condition和原来的this.wait/this.notify实现的效果一样,我干嘛还要使用它呢?
其实condition最重要的一点是,它可以配合lock实现“多路等待”(阻塞/唤醒指定类型的线程),
实现单一this.wait/this.notify它们实现不了的功能。
我们来看看JavaApi中的一个“可阻塞的队列”的底层代码实现:
作为一个示例,假定有一个绑定的缓冲区,它支持 put 和 take 方法。如果试图在空的缓冲区上执行 take 操作,则在某一个项变得可用之前,线程将一直阻塞;如果试图在满的缓冲区上执行 put 操作,则在有空间变得可用之前,线程将一直阻塞。而唤醒的时候,“取”的唤醒只会唤醒取take线程,
“放”的唤醒只会唤醒put线程。一次只通知一个线程。可以使用两个 Condition 实例来做到这一点。
上面的队列起到了“缓冲”的效果,就是在存储空间大小固定的时候,
任务来了很多时,多余的任务就会在队列中等待被拿出使用。而存取都是线程安全
(1)概述
Condition是Java有关锁的包下面的一个类,锁我们只能实现互斥,不能实现通知。而Condition就是解决这个问题,Condition的功能类似在传统线程技术中的Object.wait和Object.notify的功能。
(2)实例
记得我们之前写过的一个例子,一道面试题:
子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次,
接着再回到主线程又循环100次,如此循环50次。
代码是这样的:
package cn.edu.hpu.test;public class ThreadTest4 { public static void main(String[] args) { new ThreadTest4().init(); } public void init() { final Business business = new Business(); new Thread( new Runnable() { public void run() { for(int i=0;i<50;i++) { business.SubThread(i); } } } ).start(); for(int i=0;i<50;i++) { business.MainThread(i); } } private class Business { boolean bShouldSub = true;//这里相当于定义了控制该谁执行的一个信号灯 public synchronized void MainThread(int i) { if(bShouldSub) try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } for(int j=1;j<=100;j++) { System.out.println(Thread.currentThread().getName() + ":i=" + i +",j=" + j); } bShouldSub = true; this.notify(); } public synchronized void SubThread(int i) { if(!bShouldSub) try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } for(int j=1;j<=10;j++) { System.out.println(Thread.currentThread().getName() + ":i=" + i +",j=" + j); } bShouldSub = false; this.notify(); } }}
我们这里要对它进行该写,使用Java5的并发库中的“锁(lock)”和“条件(Condition)”,
来代替原来的“synchronized”和“wait/notify”。
改写后的部分代码:
private class Business{ Lock lock = new ReentrantLock();//线程锁对象 Condition condition = lock.newCondition();//获取锁控制下的状态对象 boolean bShouldSub = true;//这里相当于定义了控制该谁执行的一个信号灯 public void MainThread(int i) { lock.lock();//加锁 try { if (bShouldSub) try { condition.await();//条件阻塞 } catch (InterruptedException e) { e.printStackTrace(); } for (int j = 1; j <= 100; j++) { System.out.println(Thread.currentThread().getName() + ":i=" + i + ",j=" + j); } bShouldSub = true; condition.signal();//通知其它某个线程 }finally{ lock.unlock();//解锁 } } public synchronized void SubThread(int i) { lock.lock();//加锁 try { if(!bShouldSub) try { condition.await();//条件阻塞 } catch (InterruptedException e) { e.printStackTrace(); } for(int j=1;j<=10;j++) { System.out.println(Thread.currentThread().getName() + ":i=" + i +",j=" + j); } bShouldSub = false; condition.signal();//通知其它某个线程 }finally{ lock.unlock();//解锁 } }}
运行效果:
在等待Condition时,允许发生“虚假唤醒”,这通常作为对基础平台语义上的让步。对于大多数应用程序,这带来的实际影响很小,因为Condition应该总是在一个循环中被等待,并测试正在被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序的程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。
为了防止“虚假唤醒”,所以我们要把上面的
if (bShouldSub) try { condition.await();//条件阻塞 } catch (InterruptedException e) { e.printStackTrace(); } for (int j = 1; j <= 100; j++) { System.out.println(Thread.currentThread().getName() + ":i=" + i + ",j=" + j); }}改为
while(bShouldSub) try { condition.await();//条件阻塞 } catch (InterruptedException e) { e.printStackTrace(); }}for (int j = 1; j <= 100; j++) { System.out.println(Thread.currentThread().getName() + ":i=" + i + ",j=" + j);}下面子线程的也是一个道理。
(3)优势
既然condition和原来的this.wait/this.notify实现的效果一样,我干嘛还要使用它呢?
其实condition最重要的一点是,它可以配合lock实现“多路等待”(阻塞/唤醒指定类型的线程),
实现单一this.wait/this.notify它们实现不了的功能。
我们来看看JavaApi中的一个“可阻塞的队列”的底层代码实现:
作为一个示例,假定有一个绑定的缓冲区,它支持 put 和 take 方法。如果试图在空的缓冲区上执行 take 操作,则在某一个项变得可用之前,线程将一直阻塞;如果试图在满的缓冲区上执行 put 操作,则在有空间变得可用之前,线程将一直阻塞。而唤醒的时候,“取”的唤醒只会唤醒取take线程,
“放”的唤醒只会唤醒put线程。一次只通知一个线程。可以使用两个 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,当唤醒的时候,就会唤醒不必要的线程。
上面的队列起到了“缓冲”的效果,就是在存储空间大小固定的时候,
任务来了很多时,多余的任务就会在队列中等待被拿出使用。而存取都是线程安全
的,不会发生栈溢出和空指针异常的情况。
转载请注明出处:http://blog.csdn.net/acmman/article/details/52918211
0 0
- 【Java多线程与并发库】11.java5条件阻塞Condition的应用
- 【java并发】条件阻塞Condition的应用
- java5条件阻塞Condition的应用(十三)
- 【Java多线程与并发库】16.java5阻塞队列的应用
- 【张孝祥并发课程笔记】12:java5条件阻塞Condition的应用
- java5条件阻塞condition的应用(1)
- java5条件阻塞Condition的应用-多路等待通知Lock-Condition使用-笔记整理10
- (10)java5条件阻塞Condition的应用<包含阻塞队列知识>
- (10)java5条件阻塞Condition的应用<包含阻塞队列知识>
- 【Java多线程与并发库】8.java5线程并发库之线程池的应用
- 【Java多线程与并发库】8.java5线程并发库之线程池的应用
- 线程高级应用-心得7-java5线程并发库中阻塞队列Condition的应用及案例分析
- 条件阻塞Condition的应用
- 条件阻塞Condition的应用
- Java并发之读写锁Lock和条件阻塞Condition的应用
- Java并发之读写锁Lock和条件阻塞Condition的应用
- Java并发之读写锁Lock和条件阻塞Condition的应用
- 【Java多线程与并发库】17.java5同步集合类的应用
- PAC自动代理模式配置手册
- C语言二级指针(指向指针的指针)
- 灵感 Inspiration
- 对C语言指针的总结
- ASP.NET页面中实现数据柱状图
- 【Java多线程与并发库】11.java5条件阻塞Condition的应用
- AndroidStudio的Debug调试方法学习
- C语言指针与二维数组
- 集合框架
- 每步取模
- 公钥和私钥的区别
- 使用NotificationCompat兼容包来处理消息通知
- Python--operateor 模块
- 源码-Oracle数据库管理-第十二章-使用PL/SQL创建Oracle程序-Part 1(PL/SQL基础)