通过保证临界区上多个线程的相互排斥,线程同步完全可以避免竞争状态的发生,但是有时还需要线程之间的协作。有两种方式可用于线程间的通信。
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--线程间协作的更多相关文章

  1. java学习之浅谈多线程2--线程同步

    如果一个共享资源被多个线程同时访问,可能会遭到破坏.举个例子说明这个问题,假设创建并启动100个线程,每个线程都往同一个账户中添加一个便士,代码如下: import java.util.concurr ...

  2. java学习之浅谈多线程4--SwingWorker

    GUI事件处理和绘图代码在一个被称为事件分发线程的特殊线程中执行.如果一个事件需要很长的时间处理,线程就不能顾及到队列中的其他任务.为了解决这个问题,可以运行费时的任务来处理单独线程中的事件.Swin ...

  3. java学习之浅谈多线程1

    创建任务和线程 任务就是对象,为了创建任务,必须首先为任务定义一个类.任务类必须实现Runnable接口.Runnable接口非常简单,它只有一个run方法.需要实现这个方法来告诉系统线程将如何运行. ...

  4. 多线程之线程间协作的两种方式:wait、notify、notifyAll和Condition

    Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...

  5. Java学习笔记——浅谈数据结构与Java集合框架(第一篇、List)

    横看成岭侧成峰,远近高低各不同.不识庐山真面目,只缘身在此山中. --苏轼 这一块儿学的是云里雾里,咱们先从简单的入手.逐渐的拨开迷雾见太阳.本次先做List集合的三个实现类的学习笔记 List特点: ...

  6. Java学习笔记——浅谈数据结构与Java集合框架(第三篇、Map)

    桃李春风一杯酒,江湖夜雨十年灯 --寄黄几复 之前图上写错了,是Hashtable类.t是小写的,它是个很古老的类,以至于命名都没有那么规范.. HashMap HashMap就是存储key-valu ...

  7. struts 2学习笔记—浅谈struts的线程安全

    Sruts 2工作流程: Struts 1中所有的Action都只有一个实例,该Action实例会被反复使用.通过上面Struts 2 的工作流程的红色字体部分我们可以清楚看到Struts 2中每个A ...

  8. Java学习笔记——浅谈数据结构与Java集合框架(第二篇、Queue、Set)

    江南好,何处异京华. 香散翠帘多在水,绿残红叶胜于花.无事避风沙. --<纳兰词> 诗词再好,大图不能忘 上大图: 先说说栈和队列: 栈就好比手枪的弹匣,你往里面压入子弹,最先压入的子弹就 ...

  9. java多线程与线程间通信

    转自(http://blog.csdn.net/jerrying0203/article/details/45563947) 本文学习并总结java多线程与线程间通信的原理和方法,内容涉及java线程 ...

随机推荐

  1. 微信小程序 --- for循环渲染

    循环标签:wx:for <view wx:for="{{['aa','bb','cc']}}"> {{index}} {{item}} </view> 这里 ...

  2. MacBook Pro Retina 安装WIN7 - 对抗模糊及其它

    最近对虚拟机里的WIN7受够了,把整个虚拟机都删了,准备装双系统. 安装过程还是很简单的,网上教程一大堆,就是通过MAC OS X自带的BootCamp工具来管理整个安装过程,我是用外置光驱安装的,没 ...

  3. asp.net mvc(一) ----------简单封装成通用的List<T>集合

    asp.net mvc(一) 这些天开始学习asp.net mvc,用传统的asp.net已经快四的年了,刚开始接触asp.net mvc确认感觉有点不适应,主要体现在asp.net mvc的实现上. ...

  4. SaltStack实现Haproxy Nginx+php MySQL主从

    构建图如下 参考文档:https://github.com/unixhot/saltbook-code/tree/master

  5. UESTC 485 Game(康托展开,bfs打表)

    Game Time Limit: 4000/2000MS (Java/Others) Memory Limit: 65535/65535KB (Java/Others) Submit Status t ...

  6. win10配置的静态/动态IP和 DNS的方法

    1.配置静态IP和DNS netsh interface ip set address name="以太网" source=static addr=192.168.9.145 ma ...

  7. Process Monitor分析某个应用行为

    1.打开Process Mointor 2.点击filter-->filter   在弹出的对话框中Architecture 下拉框,选择Process Name 填写要分析的应用程序名字. 点 ...

  8. Java spring mvc多数据源配置

    1.首先配置两个数据库<bean id="dataSourceA" class="org.apache.commons.dbcp.BasicDataSource&q ...

  9. 20144306《网络对抗》MAL_后门原理与实践

    本期收获 1.了解后门的基本概念. 2.Netcat.socat.MSF meterpreter的使用(MSF meterpreter实在太好玩了) 3.后门软件的启动方式: Windows任务计划程 ...

  10. Python开发【模块】:torndb

    Torndb模块 概要:torndb是一个轻量级的基于MySQLdb封装的一个模块,其是tornado框架的一部分.其项目主页为:https://github.com/bdarnell/torndb ...