wait()和notifyAll()方法以一种非常低级的方式解决了任务互操作问题,即每次交互时都握手。在许多情况下,你可以瞄向更高的抽象级别,使用同步队列来解决任务协作问题,同步队列在任何时刻都只允许一个任务插入或移除元素。在java.util.concurrent.BlockingQueue接口中提供了这个队列,这个接口有大量的标准实现。你通常可以使用LinkedBlockingQueue,它是一个无界队列,还可以使用ArrayBlockingQueue,它具有固定的尺寸,因此你可以在它被阻塞之前,向其中放置有限数量的元素。
如果消费者任务试图从队列中获取对象,而该队列此时为空,那么这些队列还可以挂起消费者任务,并且当有更多的元素可用时回复消费者任务。阻塞队列可以解决非常大量的问题,而其方式与wait()和notifyAll()相比,则简单并可靠得多。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
/**
* 阻塞队列可以解决非常大量的问题,而其方式与wait()和notifyAll()相比,则简单并可靠得多。
* 下面是一个简单的测试,它将多个LiftOff对象的执行串行化了。消费者是LiftOffRunner,它将每个LiftOff对象从BlockingQueue
* 中推出并直接运行。(即,它通过显式地调用run()而使用自己的线程来运行,而不是为每个任务启动一个新线程。)
*
* @create @author Henry @date 2016-12-23
*
*/
class LiftOffRunner implements Runnable {
private BlockingQueue<LiftOff> rockets;
public LiftOffRunner(BlockingQueue<LiftOff> rockets) {
this.rockets = rockets;
}
public void add(LiftOff lo) {
try {
rockets.put(lo);
} catch (InterruptedException e) {
System.err.println("Interrupted during put()");
}
}
@Override
public void run() {
try {
System.out.println("Coming in");
while (!Thread.interrupted()) {
LiftOff rocket;
rocket = rockets.take();
rocket.run();
}
} catch (InterruptedException e) {
System.err.println("Waking from take() ,InterruptedException");
}
System.out.println("Exiting LiftOffRunner");
}
}
/**
*
* 简单的对象实现Runnable接口
*
* @create @author Henry @date 2016-11-16
*/
class LiftOff implements Runnable {
protected int countDown = 10;
private static int taskCount = 0;
private final int id = taskCount++;
public LiftOff() {
}
public LiftOff(int countDown) {
this.countDown = countDown;
}
public String status() {
return "#" + id + "(" + (countDown > 0 ? countDown + "), ": "Liftoff),\n") ;
}
@Override
public void run() {
while (countDown-- > 0) {
System.out.print(status());
/**
* Thread.yield()的调用是对线程调度器(java线程机制的一部分,可以将CPU从一个线程转移给另一个线程)的一种建议,
* 它在声明:“我已经执行完生命周期中最重要的部分了,此刻正是切换给其他任务执行一段时间的大好时机。”
* 这完全是选择性的,但是这里使用它是因为它会在这些示例中产生更加有趣的输出:你更有可能会看到任务换进换出的证据。
*
*/
Thread.yield();
}
}
}
/**
* 各个任务有main()放置到了BlockingQueue中,并且由LiftOffRunner从BlockingQueue中取出。注意,LiftOffRunner可以忽略
* 同步问题,因为它们已经由BlockingQueue解决了。
*
* @create @author Henry @date 2016-12-23
*
*/
public class TestBlockingQueues {
static void getkey() {
try {
// Compensate for Windows/Linux difference in the
// length of the result produced by the Enter key;
new BufferedReader(new InputStreamReader(System.in)).readLine();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
static void getkey(String message) {
System.out.println(message);
getkey();
}
static void test(String msg, BlockingQueue<LiftOff> queue) throws InterruptedException {
System.out.println(msg);
LiftOffRunner runner = new LiftOffRunner(queue);
Thread t = new Thread(runner);
t.start();
TimeUnit.SECONDS.sleep(2);
for (int i = 0; i < 5; i++)
runner.add(new LiftOff(5));
TimeUnit.SECONDS.sleep(3);
getkey("Press 'Enter' (" + msg + ")");
t.interrupt();
System.out.println("Finished " + msg + " test");
}
public static void main(String[] args) throws Exception {
test("LinedBlockingQueue ",new LinkedBlockingQueue<LiftOff>());
test("ArrayBlockingQueue ",new ArrayBlockingQueue<LiftOff>(3));
test("SynchronousQueue ",new SynchronousQueue<LiftOff>());
}
}
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* 吐司BlockingQueue
*
* 考虑下面这个使用BlockingQueue的示例,有一台机器具有三个任务:一个制作吐司、一个给吐司抹黄油,
* 另一个在抹过黄油的吐司上涂果酱。我们可以通过各个处理过程之间的BlockingQueue来运行这个吐司制作程序:
*
* @create @author Henry @date 2016-12-23
*
*/
class Toast {
public enum Status {
DRY, BUTTERED, JAMMED
}
private Status status = Status.DRY;
private final int id;
public Toast(int idn) {
id = idn;
}
public void butter() {
status = Status.BUTTERED;
}
public void jam() {
status = Status.JAMMED;
}
public Status getStatus() {
return status;
}
public int getId() {
return id;
}
@Override
public String toString() {
return "Toast " + id + ": " + status;
}
}
class ToastQueue extends LinkedBlockingQueue<Toast> {
}
class Toaster implements Runnable {
private ToastQueue toashtQueue;
private int count = 0;
private Random rand = new Random(47);
public Toaster(ToastQueue tq) {
toashtQueue = tq;
}
@Override
public void run() {
try {
while (!Thread.interrupted()) {
TimeUnit.MILLISECONDS.sleep(100 + rand.nextInt(500));
Toast t = new Toast(count++);
System.out.println(t);
toashtQueue.put(t);
}
} catch (InterruptedException e) {
System.err.println("Toaster interrupted");
}
System.out.println("Toaster off");
}
}
// Apply butter to toast;
class Butterer implements Runnable {
private ToastQueue dryQueue, butteredQueue;
public Butterer(ToastQueue dry, ToastQueue buttered) {
dryQueue = dry;
butteredQueue = buttered;
}
@Override
public void run() {
try {
while (!Thread.interrupted()) {
// Blocks until next piece of toast is availble;
Toast t = dryQueue.take();
t.butter();
System.out.println(t);
butteredQueue.put(t);
}
} catch (InterruptedException e) {
System.err.println("Butterer interrupted");
}
System.out.println("Butterer off");
}
}
// Apply jam to buttered toast;
class Jammer implements Runnable {
private ToastQueue butteredQueue, finishedQueue;
public Jammer(ToastQueue buttered, ToastQueue finished) {
butteredQueue = buttered;
finishedQueue = finished;
}
@Override
public void run() {
try {
while (!Thread.interrupted()) {
// Blocks until next piece of toast is available;
Toast t = butteredQueue.take();
t.jam();
System.out.println(t);
finishedQueue.put(t);
}
} catch (InterruptedException e) {
System.err.println("Jammer Interrupted");
}
System.out.println("Jammer off");
}
}
class Eater implements Runnable {
private ToastQueue finishedQueue;
private int counter = 0;
public Eater(ToastQueue finished) {
finishedQueue = finished;
}
@Override
public void run() {
try {
while (!Thread.interrupted()) {
// Blocks untill next piece of toast is available;
Toast t = finishedQueue.take();
// Verify that the toast is coming in order.
// and that all pieces are getting jammed;
if (t.getId() != counter++ || t.getStatus() != Toast.Status.JAMMED) {
System.out.println(">>> Error: " + t);
System.exit(1);
} else
System.out.println("Chomp! " + t);
}
} catch (InterruptedException e) {
System.err.println("Eater interrupted");
}
System.out.println("Eater off");
}
}
/**
* Toast 是一个使用enum值的优秀示例。注意,这个示例中没有任何显示的同步(即使用Lock对象,或synchronized 关键字的同步),
* 因为同步队列(其内部是同步的)和系统的设计隐式地管理了---每片Toast在任何时刻都由一个任务在操作。因为队列的阻塞,使得
* 处理过程将被自动地挂起和恢复。你可以看到由BlockingQueue产生的简化十分明显。在使用显式的wait()和notifyAll()时存在
* 的类和类之间的耦合被消除了,因为每个类都只和它的BlockingQueue通信。
*
* @create @author Henry @date 2016-12-23
*
*/
public class ToastOMatic {
public static void main(String[] args) throws Exception {
ToastQueue dryQueue = new ToastQueue(), butteredQueue = new ToastQueue(), finishedQueue = new ToastQueue();
ExecutorService exec=Executors.newCachedThreadPool();
exec.execute(new Toaster(dryQueue));
exec.execute(new Butterer(dryQueue,butteredQueue));
exec.execute(new Jammer(butteredQueue,finishedQueue));
exec.execute(new Eater(finishedQueue));
TimeUnit.SECONDS.sleep(5);
exec.shutdownNow();
}
}