付费节点推荐
免费节点
节点使用教程
前面通过同步锁来同步任务的行为,两个任务在交替访问共享资源的时候,可以通过使用同步锁使得任何时候只有一个任务可以访问该资源,见:线程同步之synchronized关键字。下面主要讲的是如何使任务彼此间可以协作,使得多个任务可以一起工作去解决木某个问题,因为有些问题中,某些部分必须在其他部分被解决之前解决,就像在餐厅服务员要端菜就必须有厨师做好了菜。在任务协作时,可以让任务自身挂起,直至某些外部条件发生变化,表示是时候让这个任务向前推动了为止。
wait/notify
wait方法会在等待外部世界产生变化的时候将任务挂起,并且只有在notify或notifyAll发生时即表示发生了某些感兴趣的事物,这个任务才会被唤醒并去检查所产生的变化。
wait方法表示主动释放同步锁并将任务挂起,当前任务处于waiting状态。由于会释放锁,意味着该对象的其他synchronized方法可以在wait期间被调用。而恰好在这些方法里面发生了唤醒被挂起任务所感兴趣的变化。
当调用wait方法时,就是再声明:“我已经刚刚做完能做的所有事情,因此我要在这里等待,但是我希望其他的synchronized操作在条件适合的情况下通知我让我继续执行。”
notify方法用来唤醒处于wait状态的任务,当notify/notifyAll因某个特定的锁而被调用时,只有等待这个锁的任务才会被唤醒。
注:
wait/notify/notifyAll这些方法是基类Object的一部分。因为任何Object都可以作为同步锁。并且只能在同步控制方法或者同步代码块里面才能调用这些方法,如果在非同步方法里面调用,程序能通过编译,运行的时候会报错IllegalMonitorStateException。也就是说调用这些方法必须拥有对象锁(同步锁)。
生产者消费者
在一个饭店里,有一个厨师和一个服务员,这个服务员必须等待厨师准备好膳食,当厨师准备好时,他会通知服务员,之后服务员上菜,然后返回继续等待。这是一个任务协作的实例。厨师代表生产者,而服务员代表消费者。
class Restaurant{
private Object obj;
public void put(){
while (true) {
synchronized (this) {
while (obj != null) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
obj = new Object();
System.out.print("Order up! ");
notifyAll();
}
}
}
public void get(){
while (true) {
synchronized (this) {
while (obj == null) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("waiter get meal");
obj = null;
notifyAll();
}
}
}
}
class Chef extends Thread{
private Restaurant restaurant;
public Chef(Restaurant restaurant){
this.restaurant = restaurant;
}
@Override
public void run() {
restaurant.put();
}
}
class Waiter extends Thread{
private Restaurant restaurant;
public Waiter(Restaurant restaurant){
this.restaurant = restaurant;
}
@Override
public void run() {
restaurant.get();
}
}
public class Test {
public static void main(String[] args) {
Restaurant restaurant = new Restaurant();
Chef produce = new Chef(restaurant);
Waiter consumer = new Waiter(restaurant);
produce.start();
consumer.start();
}
}
输出:
Order up! waiter get meal Order up! waiter get meal Order up! waiter get meal Order up! waiter get meal Order up! waiter get meal Order up! waiter get meal Order up! waiter get meal Order up! waiter get meal Order up! waiter get meal Order up! waiter get meal Order up! waiter get meal Order up! waiter get meal ...
Chef和Waiter通过Resaurant打交道,在两个线程中,一个线程也就是厨师线程挂起等待服务员线程消费做好的meal,他所感兴趣的事物就是obj是否为null。一旦obj为null也就是服务员将这份meal消费了,就会唤醒服务员消费这份meal。
另一个线程也就是服务员线程挂起等待厨师做好meal,他所感兴趣的事物也是obj是否为null。一旦obj不为null,他会消费这份meal,并且唤醒厨师做下一份meal。
一般,wait会被包装在while()语句中,这个语句在不断地测试正在等待的事物。可否换成if?初步看上去,一旦被唤醒,这个obj必然时刻获得的,因为任务被唤醒就是发生了其感兴趣的事物,可是,正事由于问题发生在并发应用中。其他任务有可能会在当前任务被唤醒时,突然插足拿走obj。因为常用下面wait的惯用法
while(conditiopnIsNotMet)
wait();
这样才能更加安全,保证了在退出循环之前条件得到满足。
未经允许不得转载:Bcoder资源网 » JAVA并发编程6_线程协作/生产者-消费者
评论前必须登录!
登陆 注册