Java教程是为JDK 8编写的。本页面中描述的示例和实践不利用后续版本中引入的改进,并可能使用不再可用的技术。
有关Java SE 9及其后续版本中更新的语言功能的摘要,请参阅Java语言变更。
有关所有JDK版本的新功能、增强功能以及删除或弃用选项的信息,请参阅JDK发布说明。
Thread对象传递给Executor.execute吗?这样的调用是否有意义?为什么有或者为什么没有?
回答:Thread实现了Runnable接口,所以你可以将Thread的实例传递给Executor.execute。然而,以这种方式使用Thread对象并没有意义。如果该对象直接从Thread实例化,它的run方法不会做任何事情。你可以定义一个有用的run方法的Thread子类 - 但是这样的类将实现执行器不会使用的特性。
BadThreads.java:
public class BadThreads {
static String message;
private static class CorrectorThread
extends Thread {
public void run() {
try {
sleep(1000);
} catch (InterruptedException e) {}
// 关键语句 1:
message = "Mares do eat oats.";
}
}
public static void main(String args[])
throws InterruptedException {
(new CorrectorThread()).start();
message = "Mares do not eat oats.";
Thread.sleep(2000);
// 关键语句 2:
System.out.println(message);
}
}
该应用程序应该打印出 "Mares do eat oats." 这句话。它能保证总是打印出这个结果吗?如果不能,为什么?改变两个Sleep的参数会有帮助吗?如何确保主线程能看到对message的所有更改?
解决方案: 该程序几乎总是会打印出 "Mares do eat oats." 这句话。然而,这个结果并不能保证,因为"关键语句 1"和"关键语句 2"之间没有发生在之前的关系。即使"关键语句 1"实际上在"关键语句 2"之前执行,这个发生在之前的关系是关于可见性的,而不是顺序。
有两种方法可以确保主线程能看到对message的所有更改:
CorrectorThread实例的引用。然后在引用message之前,在该实例上调用joinmessage在一个对象中。除了通过这些方法之外,不要直接引用message这两种技术都建立了必要的发生在之前的关系,使对message的更改可见。
第三种技术是将message声明为volatile。这保证了对message的任何写入(如"关键语句 1")与任何后续对message的读取(如"关键语句 2")之间存在发生在之前的关系。但它不能保证"关键语句 1"会确切地在"关键语句 2"之前发生。它们可能按顺序发生,但由于调度的不确定性和sleep的未知粒度,这并不是保证。
改变两个sleep调用的参数也无济于事,因为这对于确保发生在之前的关系没有任何帮助。
Drop
解决方案: java.util.concurrent.BlockingQueue 接口定义了一个 get 方法,如果队列为空,则会阻塞,还定义了一个 put 方法,如果队列已满,则会阻塞。这些操作与 Drop 的操作基本相同 - 只是 Drop 不是一个队列!不过,还有另一种看待 Drop 的方式:它是一个容量为零的队列。由于队列中没有空间来存储 任何 元素,每个 get 操作都会阻塞,直到对应的 take 操作,每个 take 操作都会阻塞,直到对应的 get 操作。有一种实现了这种行为的 BlockingQueue: java.util.concurrent.SynchronousQueue。
BlockingQueue 几乎可以无缝替换 Drop。在 中,主要的问题是使用 ProducerBlockingQueue 时,put 和 get 方法会抛出 InterruptedException 异常。这意味着现有的 try 必须向上移动一级:
import java.util.Random;
import java.util.concurrent.BlockingQueue;
public class Producer implements Runnable {
private BlockingQueue<String> drop;
public Producer(BlockingQueue<String> 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();
try {
for (int i = 0;
i < importantInfo.length;
i++) {
drop.put(importantInfo[i]);
Thread.sleep(random.nextInt(5000));
}
drop.put("DONE");
} catch (InterruptedException e) {}
}
}
Consumer
import java.util.Random;
import java.util.concurrent.BlockingQueue;
public class Consumer implements Runnable {
private BlockingQueue<String> drop;
public Consumer(BlockingQueue<String> drop) {
this.drop = drop;
}
public void run() {
Random random = new Random();
try {
for (String message = drop.take();
! message.equals("DONE");
message = drop.take()) {
System.out.format("接收到的消息: %s%n",
message);
Thread.sleep(random.nextInt(5000));
}
} catch (InterruptedException e) {}
}
}
生产者消费者示例drop
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
public class ProducerConsumerExample {
public static void main(String[] args) {
BlockingQueue<String> drop =
new SynchronousQueue<String> ();
(new Thread(new Producer(drop))).start();
(new Thread(new Consumer(drop))).start();
}
}