当多线程并发的去执行任务,在进入临界区时为了使线程之间互不干扰,我们可以采用锁来避免同时使用资源产生冲突,例如synchronized修饰的代码块或函数,但是当一个任务与另一个任务的协作时,我们可以采用Object类提供的wait(),notify(),notfiyAll(),方法配合synchronized使用来完成任务与任务之间的握手。(Java SE5以后还提供了reetrantlock,condition..等用于同步和协作的类,在concurrent 包这里我们先不做介绍)这些方法必须在synchronized修饰的代码块或函数中使用,如果外层没有synchronized修饰,虽然能用过编译,但是在运行时会抛出IllegalMonitorStateException异常,即当前线程在没有获得锁的情况下非法的调用这些协作的方法。
wait():在获得锁的情况下,任务通常需要等待摸个条件发生,才能继续运行,调用wait()方法的线程会将自身挂起,释放锁。直到获取相同锁的别的线程,调用notify(),notifyAll(),该线程才有能被唤醒。
notify():在获取锁的情况下,当前线程调用notify()会随机唤醒一个在等待相同锁的线程,但是不能保证被唤醒的线程会有执行权限,被唤醒的线程还是需要和别的线程竞争资源在有可能获得锁。
notifyAll():在获取锁的情况下,当前线程调用会唤醒等待同一把锁而进入沉睡的所有线程。
下面将举一个生产者和消费者的例子来进一步阐述。
例子比较简单,有一家餐馆(Restaurant类),有一个消费者(Waiter类),有一个生产者(Producer类),还有一个订单类(Order),跑2个线程,一个用来跑waiter类,一个用来跑producer类,waiter的任务:获取当前对象的锁,如果订单对象为空,则进入等待,直到producer的任务在获取waiter对象锁的情况下调用了notifyAll()该任务,得以继续执行,获取producer对象的锁,将order变量置为null,完成消费,再唤醒producer对象。
package com.thread.study;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
enum Food{
sandwich,hamburger,noodles,dumplings
}
public class Restaurant {
Order order;
Waiter waiter = new Waiter(this);
Producer producer = new Producer(this);
Thread threadone;
Thread threadtwo;
public Restaurant() {
threadone = new Thread(waiter);
threadtwo = new Thread(producer);
threadone.start();
threadtwo.start();
}
public Order randomGetFood(int orderNum){
Random random = new Random();
Food[] foods = Food.values();
int len = foods.length;
Food food = foods[random.nextInt(len)];
return new Order(orderNum, food.name());
}
class Order{
private int orderNumber;
private String food;
public Order(int orderNumber) {
super();
this.orderNumber = orderNumber;
}
public Order(int orderNumber, String food) {
super();
this.orderNumber = orderNumber;
this.food = food;
}
@Override
public String toString() {
return "Order [orderNumber=" + orderNumber + ", food=" + food + "]";
}
}
class Waiter implements Runnable{
private Restaurant rt;
public Waiter(Restaurant rt) {
super();
this.rt = rt;
}
@Override
public void run() {
while(!Thread.interrupted()){
try {
//获取当前对象的锁,如果订单对象为空,则进入等待
synchronized(this){
while(rt.order == null){
wait();
}
}
System.out.println("waiter get order " +rt.order);
//获取producer对象。消费订单,重新唤醒producer任务。
synchronized (rt.producer) {
rt.order =null;
rt.producer.notifyAll();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println("waiter interrputed");
}
}
}
}
class Producer implements Runnable{
private Restaurant rt;
private int count;
public Producer(Restaurant rt) {
super();
this.rt = rt;
}
@Override
public void run() {
while(!Thread.interrupted()){
try {
//如果订单不为空,这进入等待
synchronized(this){
while(rt.order != null){
wait();
}
}
//当有20个订单时中断所有任务
if(++count == 20){
System.out.println(" end work");
rt.threadone.interrupt();
rt.threadtwo.interrupt();
}
System.out.println("complete producer");
//获取waiter对象锁,产生订单,唤醒water任务
synchronized(rt.waiter){
rt.order = randomGetFood(count);
rt.waiter.notifyAll();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println("producer interrupted");
}
}
System.out.println("producer interrupted");
}
}
public static void main(String[] args){
new Restaurant();
}
}