06.Oracle官方并发教程之Guarded Blocks
来源:互联网 发布:库里赛季数据统计 编辑:程序博客网 时间:2024/05/13 08:14
多线程之间经常需要协同工作,最常见的方式是使用Guarded Blocks,它循环检查一个条件(通常初始值为true),直到条件发生变化才跳出循环继续执行。在使用Guarded Blocks时有以下几个步骤需要注意:
假设guardedJoy()方法必须要等待另一线程为共享变量joy设值才能继续执行。那么理论上可以用一个简单的条件循环来实现,但在等待过程中guardedJoy方法不停的检查循环条件实际上是一种资源浪费。
public void guardedJoy() { // Simple loop guard. Wastes // processor time. Don't do this! while(!joy) {} System.out.println("Joy has been achieved!"); }更加高效的方法是调用Object.wait将当前线程挂起,直到有另一线程发起事件通知(尽管通知的事件不一定是当前线程等待的事件)。
public synchronized void guardedJoy() { // This guard only loops once for each special event, which may not // be the event we're waiting for. while(!joy) { try { wait(); } catch (InterruptedException e) {} } System.out.println("Joy and efficiency have been achieved!"); }注意:一定要在循环里面调用wait方法,不要想当然的认为线程唤醒后循环条件一定发生了改变。
和其他可以暂停线程执行的方法一样,wait方法会抛出InterruptedException,在上面的例子中,因为我们关心的是joy的值,所以忽略了InterruptedException。为什么guardedJoy是synchronized方法?假设d是用来调用wait的对象,当一个线程调用d.wait,它必须要拥有d的内部锁(否则会抛出异常),获得d的内部锁的最简单方法是在一个synchronized方法里面调用wait。
当一个线程调用wait方法时,它释放锁并挂起。然后另一个线程请求并获得这个锁并调用Object.notifyAll通知所有等待该锁的线程。
public synchronized notifyJoy() { joy = true; notifyAll(); }当第二个线程释放这个该锁后,第一个线程再次请求该锁,从wait方法返回并继续执行。
注意:还有另外一个通知方法,notify(),它只会唤醒一个线程。但由于它并不允许指定哪一个线程被唤醒,所以一般只在大规模并发应用(即系统有大量相似任务的线程)中使用。因为对于大规模并发应用,我们其实并不关心哪一个线程被唤醒。
现在我们使用Guarded blocks创建一个生产者/消费者应用。这类应用需要在两个线程之间共享数据:生产者生产数据,消费者使用数据。两个线程通过共享对象通信。在这里,线程协同工作的关键是:生产者发布数据之前,消费者不能够去读取数据;消费者没有读取旧数据前,生产者不能发布新数据。
在下面的例子中,数据通过Drop对象共享的一系列文本消息:
public class Drop {private String message;private boolean empty = true;public synchronized String take() {while (empty) {try {wait();} catch (InterruptedException e) {System.out.println(e.getMessage());}}empty = true;notifyAll();return message;}public synchronized void put(String message) {while (!empty) {try {wait();} catch (InterruptedException e) {System.out.println(e.getMessage());}}empty = false;this.message = message;notifyAll();}}
Producer是生产者线程,发送一组消息,字符串DONE表示所有消息都已经发送完成。为了模拟现实情况,生产者线程还会在消息发送时随机的暂停。
public class Producer implements Runnable {private Drop drop;public Producer(Drop drop) {this.drop = drop;}@Overridepublic void run() {String importantInfo[] = { "Mares eat oats", "Does eat oats","Little lambs eat ivy", "A kid will eat ivy too" };Random random = new Random();for (int i = 0; i < importantInfo.length; i++) {drop.put(importantInfo[i]);System.out.format("MESSAGE PUT: %s%n", importantInfo[i]);try {Thread.sleep(random.nextInt(5000));} catch (InterruptedException e) {System.out.println(e.getMessage());}}drop.put("DONE");}}
Consumer是消费者线程,读取消息并打印出来,直到读取到字符串DONE为止。消费者线程在消息读取时也会随机的暂停。
public class Consumer implements Runnable {private Drop drop;public Consumer(Drop drop) {this.drop = drop;}@Overridepublic void run() {Random random = new Random();for (String message = drop.take(); !message.equals("DONE"); message = drop.take()) {System.out.format("MESSAGE RECEIVE: %s%n", message);try {Thread.sleep(random.nextInt(5000));} catch (InterruptedException e) {System.out.println(e.getMessage());}}}}
ProducerConsumerExample是主线程,它启动生产者线程和消费者线程。
public class ProducerConsumerDemo {public static void main(String[] args) { Drop drop = new Drop(); new Thread(new Producer(drop)).start(); new Thread(new Consumer(drop)).start();}}
注意:Drop类是用来演示Guarded Blocks如何工作的。为了避免重新发明轮子,当你尝试创建自己的共享数据对象时,请查看Java Collections Framework中已有的数据结构。如需更多信息,请参考Questions and Exercises。
0 0
- 06.Oracle官方并发教程之Guarded Blocks
- 01.Oracle官方并发教程之概述
- 04.Oracle官方并发教程之同步
- Oracle官方并发教程(1)
- Oracle官方并发教程(2)
- 02.Oracle官方并发教程之进程和线程
- 03.Oracle官方并发教程之线程对象
- 05.Oracle官方并发教程之活跃度
- 07.Oracle官方并发教程之不可变对象
- 并发设计模式之Guarded Suspension模式
- Guarded Blocks 保护块
- Java并发教程(Oracle官方资料)
- Java并发教程(Oracle官方资料)
- Java并发教程(Oracle官方资料)
- Java并发教程(Oracle官方资料)
- 【博文推荐】Oracle官方并发教程
- Java并发教程(Oracle官方资料)
- Java并发教程(Oracle官方资料)
- 生活是首歌而我老跑调
- javascript面对对象编程 之继承
- 用AWT写的登陆界面
- 导入/导出
- 红尘有爱,且行且珍惜
- 06.Oracle官方并发教程之Guarded Blocks
- 语音识别系统原理介绍----gmm-hmm
- Ubuntu下gcc安装及使用
- 应用内加载资源
- 2014/5/24_tomcat的配置
- Ubuntu交叉编译与路径的关系
- 红尘有爱,且行且珍惜
- 黑马程序员学习笔记_OC之NSString
- 黑马程序员:枚举