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)