【翻译十四】java-并发之保护块儿
Guarded Blocks
Threads often have to coordinate their actions. The most common coordination idiom is the guarded block. Such a block begins by polling a condition that must be true before the block can proceed. There are a number of steps to follow in order to do this correctly.
Suppose, for example guardedJoy is a method that must not proceed until a shared variable joy has been set by another thread. Such a method could, in theory, simply loop until the condition is satisfied, but that loop is wasteful, since it executes continuously while waiting.
public void guardedJoy() {
// Simple loop guard. Wastes
// processor time. Don't do this!
while(!joy) {}
System.out.println("Joy has been achieved!");
}
A more efficient guard invokes Object.wait to suspend the current thread. The invocation of wait does not return until another thread has issued a notification that some special event may have occurred — though not necessarily the event this thread is waiting for:
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!");
}
Note: Always invoke
wait inside a loop that tests for the condition being waited for. Don't assume that the interrupt was for the particular condition you were waiting for, or that the condition is still true.Like many methods that suspend execution, wait can throw InterruptedException. In this example, we can just ignore that exception — we only care about the value of joy.
Why is this version of guardedJoy synchronized? Suppose d is the object we're using to invoke wait. When a thread invokesd.wait, it must own the intrinsic lock for d — otherwise an error is thrown. Invoking wait inside a synchronized method is a simple way to acquire the intrinsic lock.
When wait is invoked, the thread releases the lock and suspends execution. At some future time, another thread will acquire the same lock and invoke Object.notifyAll, informing all threads waiting on that lock that something important has happened:
public synchronized notifyJoy() {
joy = true;
notifyAll();
}
Some time after the second thread has released the lock, the first thread reacquires the lock and resumes by returning from the invocation of wait.
Note: There is a second notification method,
notify, which wakes up a single thread. Because notify doesn't allow you to specify the thread that is woken up, it is useful only in massively parallel applications — that is, programs with a large number of threads, all doing similar chores. In such an application, you don't care which thread gets woken up.Let's use guarded blocks to create a Producer-Consumer application. This kind of application shares data between two threads: the producer, that creates the data, and the consumer, that does something with it. The two threads communicate using a shared object. Coordination is essential: the consumer thread must not attempt to retrieve the data before the producer thread has delivered it, and the producer thread must not attempt to deliver new data if the consumer hasn't retrieved the old data.
In this example, the data is a series of text messages, which are shared through an object of type :Drop
public class Drop {
// Message sent from producer
// to consumer.
private String message;
// True if consumer should wait
// for producer to send message,
// false if producer should wait for
// consumer to retrieve message.
private boolean empty = true;
public synchronized String take() {
// Wait until message is
// available.
while (empty) {
try {
wait();
} catch (InterruptedException e) {}
}
// Toggle status.
empty = true;
// Notify producer that
// status has changed.
notifyAll();
return message;
}
public synchronized void put(String message) {
// Wait until message has
// been retrieved.
while (!empty) {
try {
wait();
} catch (InterruptedException e) {}
}
// Toggle status.
empty = false;
// Store message.
this.message = message;
// Notify consumer that status
// has changed.
notifyAll();
}
}
The producer thread, defined in , sends a series of familiar messages. The string "DONE" indicates that all messages have been sent. To simulate the unpredictable nature of real-world applications, the producer thread pauses for random intervals between messages.Producer
import java.util.Random;
public class Producer implements Runnable {
private Drop drop;
public Producer(Drop drop) {
this.drop = drop;
}
public 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]);
try {
Thread.sleep(random.nextInt(5000));
} catch (InterruptedException e) {}
}
drop.put("DONE");
}
}
The consumer thread, defined in , simply retrieves the messages and prints them out, until it retrieves the "DONE" string. This thread also pauses for random intervals.Consumer
import java.util.Random;
public class Consumer implements Runnable {
private Drop drop;
public Consumer(Drop drop) {
this.drop = drop;
}
public void run() {
Random random = new Random();
for (String message = drop.take();
! message.equals("DONE");
message = drop.take()) {
System.out.format("MESSAGE RECEIVED: %s%n", message);
try {
Thread.sleep(random.nextInt(5000));
} catch (InterruptedException e) {}
}
}
}
Finally, here is the main thread, defined in , that launches the producer and consumer threads.ProducerConsumerExample
public class ProducerConsumerExample {
public static void main(String[] args) {
Drop drop = new Drop();
(new Thread(new Producer(drop))).start();
(new Thread(new Consumer(drop))).start();
}
}
Note: The
Drop class was written in order to demonstrate guarded blocks. To avoid re-inventing the wheel, examine the existing data structures in the Java Collections Framework before trying to code your own data-sharing objects. For more information, refer to the Questions and Exercises section. 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的值。
Object.notifyAll方法。告诉所有的线程,一些重要的事情已经发生了。 public synchronized notifyJoy() {
joy = true;
notifyAll();
}
许多时候当第二个线程释放了这个锁,第一个线程会重新获得锁等待返回调用。
注意:这里有第二个唤起注意的方法,notify,它唤起单个线程的注意。由于notify并不允许你指定你要唤起那个特定的线程,因此它的用处只是在大规模的并行程序中,即,在有许多线程的程序中,而且每个线程都做着简单且重要的事情,你并不关心那个线程是唤醒的。
让我们用保护块儿构造一个生产者-消费者的应用程序。这个应用程序的两个线程会共享数据:生产者,创建数据;消费者,用这些数据做一些其他的事情。这两个线程通过共享一个对象来交流。协同工作是肯定的:在生产者传递数据之前,消费者线程必须一直检索新的数据,如果消费者没有检索旧的数据,那么生产者就不能传递新的数据。
public class Drop {
// Message sent from producer
// to consumer.
private String message;
// True if consumer should wait
// for producer to send message,
// false if producer should wait for
// consumer to retrieve message.
private boolean empty = true;
public synchronized String take() {
// Wait until message is
// available.
while (empty) {
try {
wait();
} catch (InterruptedException e) {}
}
// Toggle status.
empty = true;
// Notify producer that
// status has changed.
notifyAll();
return message;
}
public synchronized void put(String message) {
// Wait until message has
// been retrieved.
while (!empty) {
try {
wait();
} catch (InterruptedException e) {}
}
// Toggle status.
empty = false;
// Store message.
this.message = message;
// Notify consumer that status
// has changed.
notifyAll();
}
}
生产者线程,在producer类中定义,产生一系列相似的消息。“DONE”表示所有的消息都已经发送。为了模拟现实世界的生产者-消费者现象,生产者线程会在发送下一个消息的时候停留随机的时间。
import java.util.Random;
public class Producer implements Runnable {
private Drop drop;
public Producer(Drop drop) {
this.drop = drop;
}
public 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]);
try {
Thread.sleep(random.nextInt(5000));
} catch (InterruptedException e) {}
}
drop.put("DONE");
}
}
消费者线程,在Consumer类中定义,只是简单的检索和打印这些消息,直到他检索到“DONE”位置,它也会停留随机的时间。
import java.util.Random;
public class Consumer implements Runnable {
private Drop drop;
public Consumer(Drop drop) {
this.drop = drop;
}
public void run() {
Random random = new Random();
for (String message = drop.take();
! message.equals("DONE");
message = drop.take()) {
System.out.format("MESSAGE RECEIVED: %s%n", message);
try {
Thread.sleep(random.nextInt(5000));
} catch (InterruptedException e) {}
}
}
}
最后,是主线程,在ProducerConsumerExample线程中定义,它载入了Producer和Consumer类。
public class ProducerConsumerExample {
public static void main(String[] args) {
Drop drop = new Drop();
(new Thread(new Producer(drop))).start();
(new Thread(new Consumer(drop))).start();
}
}
注意:写Drop类是为了展示保护块儿。为了避免重复工作,在你编写共享数据类型的时候,请参考已经有的Java Collections Framework。想了解更多详细,请参看Questions and Exercises 节。

【翻译十四】java-并发之保护块儿的更多相关文章
- Java进阶(二十四)Java List集合add与set方法原理简介
Java List集合add与set方法原理简介 add方法 add方法用于向集合列表中添加对象. 语法1 用于在列表的尾部插入指定元素.如果List集合对象由于调用add方法而发生更改,则返回 tr ...
- JDK源码阅读-------自学笔记(二十四)(java.util.LinkedList 再探 自定义讲解)
一.实现get方法 1.一般思维实现思路 1).将对象的值放入一个中间变量中. 2).遍历索引值,将中间量的下一个元素赋值给中间量. 3).返回中间量中的元素值. 4).示意图 get(2),传入角标 ...
- 【翻译十八】java-并发之锁对象
Lock Objects Synchronized code relies on a simple kind of reentrant lock. This kind of lock is easy ...
- Java 读书笔记 (十四) Java 方法
finalize() 方法 finalize() 用来清除回收对象. //为什么要回收内存?怎样写可以避免内存过多占用?什么时候需要手动回收内存? protected void finalize() ...
- Java学习笔记二十四:Java中的Object类
Java中的Object类 一:什么是Object类: Object类是所有类的父类,相当于所有类的老祖宗,如果一个类没有使用extends关键字明确标识继承另外一个类,那么这个类默认继承Object ...
- 二十四 java 多线程一些知识点
1:blocked线程和waiting的线程的区别? 如何唤醒? java线程中含有waiting与blocked两种状态: 线程的 blocked状态往往是无法进入同步方法/代码块来完成的(BLOC ...
- 菜鸡的Java笔记 第二十四 - java 接口的基本定义
1.接口的基本定义以及使用形式 2.与接口有关的设计模式的初步认识 3.接口与抽象类的区别 接口与抽象类相比,接口的使用几率是最高的,所有的 ...
- 【翻译十二】java-并发之活性
A concurrent application's ability to execute in a timely manner is known as its liveness. This sect ...
- Gradle 1.12 翻译——第十四章. 教程 - 杂七杂八
有关其它已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或訪问:http://gradledoc.qiniudn.com ...
随机推荐
- Delphi Dll 消息处理
转载:http://blog.csdn.net/lailai186/article/details/8770643 事情的导火线是GIF图片的显示. 在应用程序中, 利用三方的GIFImage.pas ...
- bootstrap按钮
按钮 基类 -btn 样式 btn-default(默认) btn-link(链接) 大小 btn-*[lg,sm,xs] 状态 active disabled <!DOCTYPE HTML&g ...
- phpcms访问顶级栏目,自动跳到第一个子栏目
在顶级栏目的category页放入如下代码: <?php if($child){ $child_arrary=explode(',',$arrchildid); $to_url=$CATEGOR ...
- C#的contextMenuStrip右键没反应的可能原因
contextMenuStrip设置右键菜单,但是新手常常忽略一个问题,我要遇到了,即没有设置contextMenuStrip所在控件的contextMenuStrip属性,需要把contextMen ...
- mysql同一台服务器上不同数据库中个别表内容同步
>>>>>>soft_wsx>>>>>>--数据备份与还原>>同步备用服务器--1.完全备份主数据库--2.使用带S ...
- C# nullable<T> 用法小结
今天在园子里看到一个关于C#中对于可空类型的描述的帖子,感觉不错于是自己写了个小例子尝试下. 在C#中,对于可空类型描述为:Nullable<T>, 它表示该类型是可以为空的一个类型.它被 ...
- .NET中六个重要的概念:栈、堆、值类型、引用类型、装箱和拆箱 (转)
作者: Edison Chou 来源: 博客园 发布时间: 2014-09-03 15:59 阅读: 318 次 推荐: 2 原文链接 [收藏] 原文作者:Shivprasad k ...
- POJ 3322(广搜)
---恢复内容开始--- http://poj.org/problem?id=3322 题意:http://jandan.net/2008/01/24/bloxorz.html就是这个鬼游戏 我也是郁 ...
- 【转】Nginx服务器的反向代理proxy_pass配置方法讲解
[转]Nginx服务器的反向代理proxy_pass配置方法讲解 转自:http://www.jb51.net/article/78746.htm 就普通的反向代理来讲Nginx的配置还是比较简单的, ...
- iOS 在UITableViewCell中加入自定义view时view的frame设定注意
由于需要重用同一个布局,于是在cellForRowAtIndexPath中把自定义view加在了cell上,我是这样设定view的frame的 var screenFrame = UIScreen.m ...