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

    在写计算机视觉与导航技术的课程作业,是关于sift和surf特征的提取及匹配.因为opencv中都有直接的函数可以调用. 关于SIFT和SURF的特征在opencv的nonfree模块中,从字面意思就 ...

  2. rac数据库单连接报错ora-12537解决办法

    1.现象如下: C:\Users\Administrator.DBA-PC>sqlplus sys/oracle@192.168.100.33:1521/orcl as sys dba SQL* ...

  3. Python量化教程 常用函数

    # -*- coding: utf-8 -*- # @Author: fangbei # @Date: 2017-08-26 # @Original: price_str = '30.14, 29.5 ...

  4. python 的 ord()、 chr()、 unichr() 函数

    一. ord() 函数描述ord() 函数是 chr() 函数(对于8位的ASCII字符串)或 unichr() 函数(对于Unicode对象)的配对函数,它以一个字符(长度为1的字符串)作为参数,返 ...

  5. mysql join实现方式

    1. nested loop join 分别从两个表读一行数据进行两两对比,复杂度是n^2 2. block nested loop join 分别从两个表读很多行数据,然后进行两两对比,复杂度也是n ...

  6. cocos2d首印象

    一. 创建工程 从 2.1.4 版本开始,官方就不再为 VS 提供模板了,逐步在各平台采用统一的 Python 脚本创建跨平台工程. 要创建工程,我们需要先从命令行进入 tools/project-c ...

  7. 每日算法之三十五:Wildcard Matching

    模式匹配的实现,'?'代表单一字符,'*'代表随意多的字符.写代码实现两个字符串是否匹配. Implement wildcard pattern matching with support for ' ...

  8. Nginx 设置临时维护页面

    Nginx 设置临时维护页面 http://www.myexception.cn/open-source/1753957.html http://blog.justwd.net/snippets/ng ...

  9. python多线程为什么不能利用多核cpu

    GIL 与 Python 线程的纠葛 GIL 是什么东西?它对我们的 python 程序会产生什么样的影响?我们先来看一个问题.运行下面这段 python 程序,CPU 占用率是多少? # 请勿在工作 ...

  10. Kconfig文件说明2

    Konfig详解: 当执行#make menuconfig时会出现内核的配置界面,所有配置工具都是通过读取"arch/$(ARCH)Kconfig"文件来生成配置界面,这个文件就是 ...