wait()和notifyAll()方法以每次交互握手的方式解决任务操作问题,concurrent类库使用BlockingQueue同步队列来解决任务的协作问题,同步队列在任何时刻只允许一个任务插入或者移除元素,这个接口有两个常用的实现LinkedBlockingQueue和ArrayBlockingQueue,前者是无界队列,后者具有固定的尺寸
如果消费者尝试获取空队列元素,那么这个队列可以挂起消费者,并在更多的元素可用时恢复消费者队列
示例
制作一个吐司需要烘干吐司,抹黄油,涂果酱三个步骤,利用BlockingQueue模拟吐司工厂生产吐司
class Toast{
public enum Status{
DRY,BUFFERED,JAMMED
}
private Status status=Status.DRY;
private final int id;
public Toast(int idn){
this.id=idn;
}
public void butter(){
status=Status.BUFFERED;
}
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 toastQueue;
private int count=0;
private Random rand=new Random(47);
public Toaster (ToastQueue tq){
this.toastQueue=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);
toastQueue.put(t);
}
} catch (Exception e) {
System.out.println("ToasterInterruptException ");
}
System.out.println("Toaster off");
}
}
class Butter implements Runnable{
private ToastQueue dryQueue,butteredQueue;
public Butter(ToastQueue dry,ToastQueue buttered) {
dryQueue=dry;
butteredQueue=buttered;
}
@Override
public void run() {
try {
while (!Thread.interrupted()) {
Toast t=dryQueue.take();
t.butter();
System.out.println(t);
butteredQueue.put(t);
}
} catch (Exception e) {
System.out.println("Butterer interrupted ");
}
System.out.println("Butterer off");
}
}
class Jammer implements Runnable{
private ToastQueue butteredQueue,finishedQueue;
public Jammer(ToastQueue butteredQueue,ToastQueue finishedQueue){
this.butteredQueue=butteredQueue;
this.finishedQueue=finishedQueue;
}
@Override
public void run() {
try {
while (!Thread.interrupted()) {
Toast t=butteredQueue.take();
t.jam();
System.out.println(t);
finishedQueue.put(t);
}
} catch (Exception e) {
System.out.println("Jammer Interrupted");
}
System.out.println("Jammer Off");
}
}
class Eater implements Runnable{
private ToastQueue finishedQueue;
private int counter;
public Eater(ToastQueue finished){
this.finishedQueue=finished;
}
@Override
public void run() {
try {
while (!Thread.interrupted()) {
Toast t=finishedQueue.take();
if (t.getId()!=counter++||t.getStatus()!=Toast.Status.JAMMED) {
System.err.println(">>>> Error:"+t);
System.exit(1);
}else {
System.out.println("Chomp!"+t);
}
}
} catch (Exception e) {
System.out.println("Easter interrupted");
}
System.out.println("Easter Off");
}
}
public class ToastOMatic {
public static void main(String[] args) throws InterruptedException {
ToastQueue dryQueue=new ToastQueue(),
butteredQueue=new ToastQueue(),
finishedQueue=new ToastQueue();
ExecutorService exec=Executors.newCachedThreadPool();
exec.execute(new Toaster(dryQueue));
exec.execute(new Butter(dryQueue, butteredQueue));
exec.execute(new Jammer(butteredQueue, finishedQueue));
exec.execute(new Eater(finishedQueue));
TimeUnit.SECONDS.sleep(5);
exec.shutdownNow();
}
}
从示例2可以看到,利用BlockingQueue避免了显示的使用wait,notify等方法
有时候需要利用输入输出在线程间通信,它们在Java输入输出类库中的对应物是PipedReader和PipedWriter,PipedReader是可中断的,interrupt()可以打断阻塞,而System.in.read()是不可中断的
示例:
利用PipedReader和PipedWriter打印26个大小写字母
class Sender implements Runnable{
private Random random=new Random(47);
private PipedWriter out=new PipedWriter();
public PipedWriter getPipedWriter(){
return out;
}
@Override
public void run() {
try {
while (true) {
for (char c='A';c<='z';c++) {
out.write(c);
TimeUnit.MILLISECONDS.sleep(random.nextInt(500));
}
}
} catch (IOException e) {
System.out.println("Sender-IOException");
} catch (InterruptedException e) {
System.out.println("Sender-InterruptedException");
}
}
}
class Receiver implements Runnable{
private PipedReader in;
public Receiver(Sender sender) throws IOException{
in=new PipedReader(sender.getPipedWriter());
}
@Override
public void run() {
try {
while (true) {
System.out.println("Read:"+(char)in.read()+",");
}
} catch (Exception e) {
System.out.println("Receiver-IOException");
}
}
}
public class PipedIO {
public static void main(String[] args) throws Exception {
Sender sender=new Sender();
Receiver receiver=new Receiver(sender);
ExecutorService exec=Executors.newCachedThreadPool();
exec.execute(sender);
exec.execute(receiver);
TimeUnit.SECONDS.sleep(20);
exec.shutdownNow();
}
}
示例
利用BlockingQueue重写大小写字母示例
class Sender2 implements Runnable{
private Random random=new Random(47);
BlockingQueuebq;
public Sender2(BlockingQueuebq){
this.bq=bq;
}
@Override
public void run() {
try {
while (true) {
for (char c='A';c<='z';c++) {
bq.add(c);
TimeUnit.MILLISECONDS.sleep(random.nextInt(500));
}
}
} catch (InterruptedException e) {
System.out.println("Sender2-InterruptedException");
}
}
}
class Receiver2 implements Runnable{
BlockingQueuebq;
public Receiver2(BlockingQueuebq){
this.bq=bq;
}
@Override
public void run() {
try {
while (!Thread.interrupted()) {
char c=bq.take();
System.out.println("receive:"+c);
}
} catch (InterruptedException e) {
System.out.println("Receiver2-InterruptedException");
}
}
}
public class PipedIOBlockingQueue {
public static void main(String[] args) throws InterruptedException {
ExecutorService exec=Executors.newCachedThreadPool();
BlockingQueuequeue=new LinkedBlockingQueue();
exec.execute(new Sender2(queue));
exec.execute(new Receiver2(queue));
TimeUnit.SECONDS.sleep(5);
exec.shutdownNow();
}
}