The Java Tutorials have been written for JDK 8.Java教程是为JDK 8编写的。Examples and practices described in this page don't take advantage of improvements introduced in later releases and might use technology no longer available.本页中描述的示例和实践没有利用后续版本中引入的改进,并且可能使用不再可用的技术。See Java Language Changes for a summary of updated language features in Java SE 9 and subsequent releases.有关Java SE 9及其后续版本中更新的语言特性的摘要,请参阅Java语言更改。
See JDK Release Notes for information about new features, enhancements, and removed or deprecated options for all JDK releases.有关所有JDK版本的新功能、增强功能以及已删除或不推荐的选项的信息,请参阅JDK发行说明。
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. 这样的块首先轮询一个条件,该条件必须为true
才能继续。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. guardedJoy
是一个方法,在另一个线程设置共享变量joy
之前,该方法不能继续。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. Object.wait
以挂起当前线程。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: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
inside a loop that tests for the condition being waited for. wait
。Like many methods that suspend execution, 像许多暂停执行的方法一样,wait
can throw InterruptedException
. wait
可以抛出InterruptedException
。In this example, we can just ignore that exception we only care about the value of 在这个例子中,我们可以忽略这个异常我们只关心joy
.joy
的值。
Why is this version of 为什么这个版本的guardedJoy
synchronized? guardedJoy
是同步的?Suppose 假设d
is the object we're using to invoke wait
. d
是我们用来调用wait
的对象。When a thread invokes 当线程调用d.wait
, it must own the intrinsic lock for d
otherwise an error is thrown. d.wait
时,它必须拥有d
的内部锁否则将抛出一个错误。Invoking 在同步方法中调用wait
inside a synchronized method is a simple way to acquire the intrinsic lock.wait
是获取内部锁的一种简单方法。
When 调用wait
is invoked, the thread releases the lock and suspends execution. wait
时,线程释放锁并暂停执行。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:Object.notifyAll
,通知等待该锁的所有线程发生了重要的事情:
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
.wait
返回来重新获得锁并恢复。
notify
, which wakes up a single thread. notify
,它唤醒单个线程。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. notify
不允许指定唤醒的线程,所以它只在大规模并行应用程序中有用;也就是说,有大量线程的程序都在做类似的杂务。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
类型的对象共享: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 Producer
, sends a series of familiar messages. Producer
中定义的producer
线程发送一系列熟悉的消息。The string "DONE" indicates that all messages have been sent. 字符串“DONE”表示所有消息都已发送。To simulate the unpredictable nature of real-world applications, the producer thread pauses for random intervals between messages.为了模拟现实世界应用程序的不可预测性,生产者线程暂停消息之间的随机间隔。
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 在Consumer
, simply retrieves the messages and prints them out, until it retrieves the "DONE" string. Consumer
中定义的consumer
线程只检索消息并将其打印出来,直到检索到“DONE”字符串。This thread also pauses for random intervals.此线程也会暂停随机间隔。
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 最后,这里是在ProducerConsumerExample
, 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(); } }
Drop
class was written in order to demonstrate guarded blocks. Drop
类是为了演示受保护的块。