java学习之浅谈多线程3--线程间协作
通过保证临界区上多个线程的相互排斥,线程同步完全可以避免竞争状态的发生,但是有时还需要线程之间的协作。有两种方式可用于线程间的通信。
1.使用条件Condition
Condition接口:
+await():void 当前线程等待直到发生某个条件
+signal():void 唤醒一个等待线程
+signalAll():Condition 唤醒所有等待线程
条件是通过调用Lock对象的newCondition()方法而创建的对象,为了使用条件,必须首先获取锁,await()方法让线程等待并且自动释放条件上的锁,一旦条件正确,线程重新获取锁并且继续执行。创建了条件,该对象就可以使用await(),signal()和signalAll()方法。
考虑典型的消费者/生产者例子。假设使用缓冲区储存整数。缓冲区的大小是受限的。缓冲区提供write(int)方法将一个int值添加到缓冲区中,还提供方法read()从缓冲区中读取和删除一个int值为了同步这个操作,使用具有两个条件的锁:notEmpty(即缓冲区非空)和notFull(即缓冲区未满)。当任务向缓冲区添加一个int时,如果缓冲区是满的,那么任务将会等待notFull状态。当任务从缓冲区中删除一个int时,如果缓冲区是空的,那么任务将等待notEmpty状态。代码如下:
import java.util.LinkedList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class ConsumerProducer {
private static Buffer buffer = new Buffer(); //创建缓冲区 public static void main(String[] args) {
//创建线程池
ExecutorService executor = Executors.newFixedThreadPool(2); executor.execute(new ProducerTask());
executor.execute(new ConsumerTask()); executor.shutdown();
} private static class ProducerTask implements Runnable {
@Override
public void run() {
try {
int i = 1;
while(true) {
System.out.println("Producer writes " + i) ;
buffer.write(i++);
Thread.sleep((int)(Math.random() * 10000));
}
} catch (InterruptedException e) {
}
}
} private static class ConsumerTask implements Runnable {
@Override
public void run() {
try {
while(true) {
System.out.println("\t\t\tConsumer reads " + buffer.read());
Thread.sleep((int)(Math.random() * 10000));
}
} catch (InterruptedException e) { }
}
} /**
* 创建内部类Buffer作为缓冲区
* @author zx
*
*/
private static class Buffer {
private static final int CAPACITY = 1; //缓冲区的大小
private LinkedList<Integer> queue = new LinkedList<Integer>(); private static Lock lock = new ReentrantLock(); //创建一个锁 //创建两个条件
private static Condition notEmpty = lock.newCondition(); //缓冲区非空
private static Condition notFull = lock.newCondition(); //缓冲区未满 public void write(int value) {
lock.lock(); //获得锁 try {
while(queue.size() == CAPACITY) {
System.out.println("Wait for notFull condition");
notFull.await(); //暂停当前线程,等待notFull条件
} queue.offer(value); //将整数value加入缓冲区
notEmpty.signal(); //唤醒等待notEmpty条件的线程
} catch (InterruptedException e) {
} finally {
lock.unlock(); //释放锁
}
} @SuppressWarnings("finally")
public int read() {
int value = 0;
lock.lock(); //获得锁
try {
while(queue.isEmpty()) {
System.out.println("\t\tWait for notEmpty condition");
notEmpty.await(); //暂停线程等待notEmpty条件
} value = queue.remove(); //从缓冲区删除一个整数
notFull.signal(); //唤醒等待notFull条件的线程
} catch (InterruptedException e) {
} finally {
lock.unlock(); //释放锁
return value;
}
} }
}
2.使用阻塞队列
阻塞队列在试图向一个满队列添加元素或者从空队列中删除元素时会导致线程阻塞。BlockingQueue接口扩展java.util.Queue,并且提供同步的put和take方法向队列头添加元素,以及从队列尾删除元素。
Java支持三个具体的阻塞队列ArrayBlockingQueue、LinkedBlockingQueue和PriorityBlockingQueue。
ArrayBlockingQueue使用数组实现阻塞队列。必须指定一个容量或者可选的公平性来构造ArrayBlockingQueue。
LinkedBlockingQueue使用链表实现阻塞队列。可以创建不受限的或者受限的LinkedBlockingQueue。
PriorityBlockingQueue是优先队列,可以创建不受限的或者受限的优先队列。
下面的程序使用ArrayBlockingQueue来简化上面的生产者/消费者程序。创建一个ArrayBlockingQueue来存储整数,生产者线程将一个整数放入队列中,而消费者线程从队列中取走一个整数。
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class ConsumerProducerUsingBlockingQueue {
//创建buffer用来存储整数
private static ArrayBlockingQueue<Integer> buffer =
new ArrayBlockingQueue<Integer>(2); public static void main(String[] args) {
//创建线程池
ExecutorService executor = Executors.newFixedThreadPool(2); executor.execute(new ProducerTask());
executor.execute(new ConsumerTask()); executor.shutdown();
} private static class ProducerTask implements Runnable {
@Override
public void run() {
try {
int i = 1;
while(true) {
System.out.println("Producer writes " + i);
buffer.put(i++); //向队列添加元素
Thread.sleep((int)(Math.random() * 10000));
}
} catch (InterruptedException e) {
}
}
} private static class ConsumerTask implements Runnable {
@Override
public void run() {
try {
while(true) {
//从队列删除元素
System.out.println("\t\t\tConsumer reads " + buffer.take());
Thread.sleep((int)(Math.random() * 10000));
}
} catch (InterruptedException e) {
}
}
}
}
java学习之浅谈多线程3--线程间协作的更多相关文章
- java学习之浅谈多线程2--线程同步
如果一个共享资源被多个线程同时访问,可能会遭到破坏.举个例子说明这个问题,假设创建并启动100个线程,每个线程都往同一个账户中添加一个便士,代码如下: import java.util.concurr ...
- java学习之浅谈多线程4--SwingWorker
GUI事件处理和绘图代码在一个被称为事件分发线程的特殊线程中执行.如果一个事件需要很长的时间处理,线程就不能顾及到队列中的其他任务.为了解决这个问题,可以运行费时的任务来处理单独线程中的事件.Swin ...
- java学习之浅谈多线程1
创建任务和线程 任务就是对象,为了创建任务,必须首先为任务定义一个类.任务类必须实现Runnable接口.Runnable接口非常简单,它只有一个run方法.需要实现这个方法来告诉系统线程将如何运行. ...
- 多线程之线程间协作的两种方式:wait、notify、notifyAll和Condition
Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...
- Java学习笔记——浅谈数据结构与Java集合框架(第一篇、List)
横看成岭侧成峰,远近高低各不同.不识庐山真面目,只缘身在此山中. --苏轼 这一块儿学的是云里雾里,咱们先从简单的入手.逐渐的拨开迷雾见太阳.本次先做List集合的三个实现类的学习笔记 List特点: ...
- Java学习笔记——浅谈数据结构与Java集合框架(第三篇、Map)
桃李春风一杯酒,江湖夜雨十年灯 --寄黄几复 之前图上写错了,是Hashtable类.t是小写的,它是个很古老的类,以至于命名都没有那么规范.. HashMap HashMap就是存储key-valu ...
- struts 2学习笔记—浅谈struts的线程安全
Sruts 2工作流程: Struts 1中所有的Action都只有一个实例,该Action实例会被反复使用.通过上面Struts 2 的工作流程的红色字体部分我们可以清楚看到Struts 2中每个A ...
- Java学习笔记——浅谈数据结构与Java集合框架(第二篇、Queue、Set)
江南好,何处异京华. 香散翠帘多在水,绿残红叶胜于花.无事避风沙. --<纳兰词> 诗词再好,大图不能忘 上大图: 先说说栈和队列: 栈就好比手枪的弹匣,你往里面压入子弹,最先压入的子弹就 ...
- java多线程与线程间通信
转自(http://blog.csdn.net/jerrying0203/article/details/45563947) 本文学习并总结java多线程与线程间通信的原理和方法,内容涉及java线程 ...
随机推荐
- Dij二级最短路
hdu1245 Saving James Bond Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 65536/32768 K (Jav ...
- 修改bootstrap 的全局样式,bootstrap 3.0 是由html5和CSS 3组成的
方法一: 不建议使用 * {}选择器,因为在一些其他样式插件.特殊部分会有更好的字体样式设定,用*就会全部覆盖. 正常引入bootstrap的css样式后,记得将自定义的样式表放到其之后, <l ...
- Solutions for common Android development problems with the Eclipse IDE- Tutorial
Table of Contents 1. Solving typical Android development problems 1.1. Clean Project 1.2. android.co ...
- 一个用于提取简体中文字符串中省,市和区并能够进行映射,检验和简单绘图的python模块
简介 一个用于提取简体中文字符串中省,市和区并能够进行映射,检验和简单绘图的python模块. 举个例子: ["徐汇区虹漕路461号58号楼5楼", "泉州市洛江区万安塘 ...
- 提高myEclipse的开发效率和外观,这些你都设置了吗?
[前言] 为什么我的myeclipse开启速度那么慢,为什么别人能哗啦啦几个快捷键打出一片代码?刚开始使用myeclipse的时候,相信大家都有这个疑问,慢慢的,也懂得一些配置,我也不例外,在此,把常 ...
- 【查看】mysql 常规书写注意事项(那些坑)
mysql 常规书写注意事项,mysql注意事项 1. 注释: -- 后面一定要加一个空格,否则会报错 2.注释:/*! content */ 在mysql中是会执行的,但是其他数据库不会. 例 ...
- 当IDENTITY_INSERT设置为OFF时不能向表插入显示值。(源:MSSQLServer,错误码:544)
错误提示"事务和快照同步时提示:当IDENTITY_INSERT设置为OFF时不能向表插入显示值.(源:MSSQLServer,错误码:544)" 原因:在SQL2008同步时到S ...
- (转) SpringBoot非官方教程 | 第十一篇:springboot集成swagger2,构建优雅的Restful API
swagger,中文“拽”的意思.它是一个功能强大的api框架,它的集成非常简单,不仅提供了在线文档的查阅,而且还提供了在线文档的测试.另外swagger很容易构建restful风格的api,简单优雅 ...
- 开启Virtualization Technology
怎样开启Virtualization Technology功能 在Win7 或linux x64位系统里装vm,然后新建虚机安装Windows Server 2008 R2 提示一些警告,无法新建虚拟 ...
- 非线性方程(组):一维非线性方程(二)插值迭代方法 [MATLAB]
一般而言,方程没有能够普遍求解的silver bullet,但是有几类方程的求解方法已经非常清晰确凿了,比如线性方程.二次方程或一次分式.一次方程可以直接通过四则运算反解出答案,二次方程的求根公式也给 ...