Java多线程 之 生产者、消费者(十三)

1.为什么要使用while循环来包围wait调用?
如果有多个waiter在竞争一个厨师的“菜”,一个waiter抢到“菜”之后,其他人就不能再抢到菜。当另一个再去判断while条件时,可能已经被那个抢到的waiter将条件置为满足了,这时不得不又wait。但是,如果这时那个抢到的waiter没有执行到更改条件怎么办呢?从这个示例代码上好像无法解释,等以后看到合适的代码再来阐述。
2.一定要在“有锁”的环境下使用wait、notify、notifyAll操作。
3.在哪个对象锁上调用wait操作,就要在哪个对象锁上调用notify、notifyAll操作。
4.关于interrupt中断
当处于sleep、wait状态时会被中断,这时线程抛出InterruptedExeception异常退出,但是也有可能程序走到while循环判断Thread.interrupted()处正常退出。
《Thinking in java》第709页所给出的生产者、消费者示例代码如下:

package org.fan.learn.thread.share;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
 * Created by fan on 2016/7/5.
 */
class Meal {
    private final int orderNum;
    Meal(int orderNum) {
        this.orderNum = orderNum;
    }
    @Override
    public String toString() {
        return " Meal : " + orderNum ;
    }
}
//服务员任务
//当没有meal时需要等待厨师做饭,当上菜之后,通知厨师做饭
class Waiter implements Runnable {
    Restaurant restaurant;
    public Waiter(Restaurant restaurant) {
        this.restaurant = restaurant;
    }
    public void run() {
        try {
            while (!Thread.interrupted()) {
                //这里锁定的waiter对象,因此在唤醒时,只能是waiter对象的notify
                synchronized (this) {
                    while (restaurant.meal == null) {
                        wait();
                    }
                }
                System.out.println("Waiter get " + restaurant.meal);
                synchronized (restaurant.chef) {
                    restaurant.meal = null;
                    //仅仅调用notifyAll会抛出异常:IllegalMonitorStateException
                    //notifyAll();
                    //唤醒chef对象的wait
                    restaurant.chef.notifyAll();
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class Chef implements Runnable {
    Restaurant restaurant;
    int count;
    public Chef(Restaurant restaurant) {
        this.restaurant = restaurant;
    }
    public void run() {
        try {
            while (!Thread.interrupted()) {
                synchronized (this) {
                    //这里锁定的是chef对象,因此在唤醒时,只能是chef对象的notify
                    while (restaurant.meal != null) {
                        wait();
                    }
                }
                System.out.println("Order up");
                if (++count == 10) {
                    System.out.println("exe shutdownNow");
                    restaurant.exe.shutdownNow(); //向所有任务发送interrupt
                }
                synchronized (restaurant.waiter) {
                    restaurant.meal = new Meal(count);
                    //唤醒waiter对象的wait
                    restaurant.waiter.notifyAll();
                    System.out.println("**********");
                }
                //睡眠有可能被中断
                TimeUnit.MILLISECONDS.sleep(100);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class Restaurant {
    Meal meal;
    //waiter、chef对象共享该restaurant对象。
    Waiter waiter = new Waiter(this);
    Chef chef = new Chef(this);
    ExecutorService exe = Executors.newCachedThreadPool();
    Restaurant () {
        exe.execute(waiter);
        exe.execute(chef);
    }
    public static void main(String[] args) {
        new Restaurant();
    }
}

下面是各种情况的输出:
(1)如果只是调用notifyAll会发生下面的错误:

Order up
Exception in thread "pool-1-thread-2" java.lang.IllegalMonitorStateException
    at java.lang.Object.notifyAll(Native Method)
    at org.fan.learn.thread.Chef.run(Restaurant.java:74)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

这是因为wait,notify,notifyAll都需要工作在“有锁”的环境下。
(2)此时还没有为chef编写sleep语句
执行shutdownNow之后正常结束

Order up
**********
Waiter get  Meal : 1
Order up
**********
Waiter get  Meal : 2
Order up
**********
Waiter get  Meal : 3
Order up
**********
Waiter get  Meal : 4
Order up
**********
Waiter get  Meal : 5
Order up
**********
Waiter get  Meal : 6
Order up
**********
Waiter get  Meal : 7
Order up
**********
Waiter get  Meal : 8
Order up
**********
Waiter get  Meal : 9
Order up
exe shutdownNow
**********
Waiter get  Meal : 10

这种情况,waiter恰好等到最后一个“菜”,上完这个菜之后,下班,正常退出。
(3)此时还没有为chef编写sleep语句
执行shutdownNow之后,waiter处于wait状态,被中断

Order up
**********
Waiter get  Meal : 1
Order up
**********
Waiter get  Meal : 2
Order up
**********
Waiter get  Meal : 3
Order up
**********
Waiter get  Meal : 4
Order up
**********
Waiter get  Meal : 5
Order up
**********
Waiter get  Meal : 6
Order up
**********
Waiter get  Meal : 7
Order up
**********
Waiter get  Meal : 8
Order up
**********
Waiter get  Meal : 9
Order up
exe shutdownNow
**********
java.lang.InterruptedException
    at java.lang.Object.wait(Native Method)
    at java.lang.Object.wait(Object.java:502)
    at org.fan.learn.thread.Waiter.run(Restaurant.java:35)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

这种情况,waiter没能等到最后一个“菜”,被中断退出。
(4)此时还没有为chef编写sleep语句
执行shutdownNow之后,waiter处于wait状态,被中断,但是输出很杂乱

java.lang.InterruptedException
Order up
    at java.lang.Object.wait(Native Method)
**********
    at java.lang.Object.wait(Object.java:502)
Waiter get  Meal : 1
    at org.fan.learn.thread.Waiter.run(Restaurant.java:35)
Order up
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
**********
Waiter get  Meal : 2
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
Order up
    at java.lang.Thread.run(Thread.java:745)
**********
Waiter get  Meal : 3
Order up
**********
Waiter get  Meal : 4
Order up
**********
Waiter get  Meal : 5
Order up
**********
Waiter get  Meal : 6
Order up
**********
Waiter get  Meal : 7
Order up
**********
Waiter get  Meal : 8
Order up
**********
Waiter get  Meal : 9
Order up
exe shutdownNow
**********

(5)执行shutdownNow之后,waiter处于wait状态,被中断,chef处于sleep状态,被中断

Order up
**********
Waiter get  Meal : 1
Order up
**********
Waiter get  Meal : 2
Order up
**********
Waiter get  Meal : 3
Order up
**********
Waiter get  Meal : 4
Order up
**********
Waiter get  Meal : 5
Order up
**********
Waiter get  Meal : 6
Order up
**********
Waiter get  Meal : 7
Order up
**********
Waiter get  Meal : 8
Order up
**********
Waiter get  Meal : 9
Order up
java.lang.InterruptedException
exe shutdownNow
    at java.lang.Object.wait(Native Method)
**********
    at java.lang.Object.wait(Object.java:502)
    at org.fan.learn.thread.Waiter.run(Restaurant.java:36)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at java.lang.Thread.sleep(Thread.java:340)
    at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
    at org.fan.learn.thread.Chef.run(Restaurant.java:78)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值