您的位置:首页 > 编程语言 > Java开发

Java线程(2)线程协作-生产者/消费者模式

2015-08-05 22:40 387 查看
文章来源:http://blog.sina.com.cn/s/blog_6560efc9010185ej.html

上一篇讲述了线程的互斥(同步),但是在很多情况下,仅仅同步是不够的,还需要线程与线程协作(通信),生产者/消费者模式是一个经典的线程同步以及通信的模型。

假设有这样一种情况,有一个盘子,盘子里只能放一个鸡蛋,A线程专门往盘子里放鸡蛋,如果盘子里有鸡蛋,则一直等到盘子里没鸡蛋,B线程专门从盘子里取鸡蛋,如果盘子里没鸡蛋,则一直等到盘子里有鸡蛋。这里盘子是一个互斥区,每次放鸡蛋是互斥的,每次取鸡蛋也是互斥的,A线程放鸡蛋,如果这时B线程要取鸡蛋,由于A没有释放锁,B线程处于等待状态,进入阻塞队列,放鸡蛋之后,要通知B线程取鸡蛋,B线程进入就绪队列,反过来,B线程取鸡蛋,如果A线程要放鸡蛋,由于B线程没有释放锁,A线程处于等待状态,进入阻塞队列,取鸡蛋之后,要通知A线程放鸡蛋,A线程进入就绪队列。我们希望当盘子里有鸡蛋时,A线程阻塞,B线程就绪,盘子里没鸡蛋时,A线程就绪,B线程阻塞,代码如下:

import java.util.ArrayList;

import java.util.List;

public class Plate {

List eggs = new ArrayList();

public synchronized Object getEgg() {

while (eggs.size() == 0) {

try {

wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

Object egg = eggs.get(0);

eggs.clear();// 清空盘子

notify();// 唤醒阻塞队列的某线程到就绪队列

System.out.println("拿到鸡蛋");

return egg;

}

public synchronized void putEgg(Object egg) {

while (eggs.size() > 0) {

try {

wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

eggs.add(egg);// 往盘子里放鸡蛋

notify();// 唤醒阻塞队列的某线程到就绪队列

System.out.println("放入鸡蛋");

}

static class AddThread extends Thread {

private Plate plate;

private Object egg = new Object();

public AddThread(Plate plate) {

this.plate = plate;

}

public void run() {

plate.putEgg(egg);

}

}

static class GetThread extends Thread {

private Plate plate;

public GetThread(Plate plate) {

this.plate = plate;

}

public void run() {

plate.getEgg();

}

}

public static void main(String args[]) {

Plate plate = new Plate();

for(int i = 0; i < 10; i++) {

new Thread(new AddThread(plate)).start();

new Thread(new GetThread(plate)).start();

}

}

}

输出结果:

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

8 l程序开始,A线程判断盘子是否为空,放入一个鸡蛋,并且唤醒在阻塞队列的一个线程,阻塞队列为空;假设CPU又调度了一个A线程,盘子非空,执行等待,这个A线程进入阻塞队列;然后一个B线程执行,盘子非空,取走鸡蛋,并唤醒阻塞队列的A线程,A线程进入就绪队列,此时就绪队列就一个A线程,马上执行,放入鸡蛋;如果再来A线程重复第一步,在来B线程重复第二步,整个过程就是生产者(A线程)生产鸡蛋,消费者(B线程)消费鸡蛋。

前段时间看了张孝祥老师线程的视频,讲述了一个其学员的面试题,也是线程通信的,在此也分享一下。

题目:子线程循环10次,主线程循环100次,如此循环100次,好像是空中网的笔试题。

public class ThreadTest2 {

public static void main(String[] args) {

final Business business = new Business();

new Thread(new Runnable() {

@Override

public void run() {

threadExecute(business, "sub");

}

}).start();

threadExecute(business, "main");

}

public static void threadExecute(Business business, String threadType) {

for(int i = 0; i < 100; i++) {

try {

if("main".equals(threadType)) {

business.main(i);

} else {

business.sub(i);

}

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

class Business {

private boolean bool = true;

public synchronized void main(int loop) throws InterruptedException {

while(bool) {

this.wait();

}

for(int i = 0; i < 100; i++) {

System.out.println("main thread seq of " + i + ", loop of " + loop);

}

bool = true;

this.notify();

}

public synchronized void sub(int loop) throws InterruptedException {

while(!bool) {

this.wait();

}

for(int i = 0; i < 10; i++) {

System.out.println("sub thread seq of " + i + ", loop of " + loop);

}

bool = false;

this.notify();

}

}

大家注意到没有,在调用wait方法时,都是用while判断条件的,而不是if,在wait方法说明中,也推荐使用while,因为在某些特定的情况下,线程有可能被假唤醒,使用while会循环检测更稳妥
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: