说明:
if和notify的情况:
to拿到执行权,if(flag)为假,生产烤鸭1,count为2,置flag为true,notify一次,若t0继续拿执行权,if(flag)为true,被wait,t1执行,if(flag)为true,被wait,t2执行,if(!flag)为假,消费了烤鸭1,置flag为false,唤醒t0,t1中的一个,假设t0被唤醒,t3执行,if(!flag)为true,被wait(),t2执行,if(!flag)为true,也被wait(),这时活着的线程只有t0,t0执行,此时不再判断if,生产了烤鸭2,count为3,flag为true,notify随机唤醒一个,若唤醒了t1,t1也不判断if,生产了烤鸭3,若唤醒t2,t2消费了烤鸭3,这时烤鸭2未被消费,此时烤鸭2没有消费者,就出现了生产出的烤鸭无人消费的问题。
结果打印如:
生产烤鸭1
...消费烤鸭1
生产烤鸭2 //烤鸭2没有被消费
生产烤鸭3
...消费烤鸭3
while和notify的情况:
继续上面的说法,此时t1,t2,t3被wait,只有t0活着,flag为false,t0被唤醒后,需要判断while(flag),为假,生产烤鸭4,置flag为true,t0唤醒t1,t0继续执行,while(flag)为true,t0被wait,t1被唤醒后也判断while(flag)为true,被wait,这时4个线程都被wait了,造成死锁。
While和notifyAll解决了问题,但效率降低了,为此,JDK1.5提供了解决方案。
14-5,多生产者多消费者问题-JDK1.5性特性-Lock
同步代码块对于锁的操作时隐式的。
JDK1.5以后将同步和锁封装成了对象,并将操作锁的隐式方式定义到了该对象中,将隐式动作变成了显示动作。
Lock接口在java.util.concurrent.locks包中,用Lock需要导入java.util.concurrent.locks.*;
标准写法:
//Lock是接口,ReentrantLock是Lock的子类,实现了Lock
Lock lock = new ReentrantLock();
public void show() {
lock.lock();
try {
//代码中可能会抛出异常,用try处理,抛出异常后会导致程序跳转,这样就不能释放锁了
//释放锁是必须执行的,所以放在finally内。
code...
}finally {
//释放锁
lock.unlock();
}
}
14-6,JDK1.5新特性-Condition
1,Lock接口:替代了同步代码或者同步函数,将同步的隐式锁操作变成显示所操作,同时更为灵活,可以在一个锁上加多组监视器。
lock()方法获取锁。
unlock()方法释放锁,通常定义在finally代码块中。
2,Condition接口:替代了Object中的wait(),notify(),notifyAll()方法,将这些监视器方法单独进行了封装,变成Condition监视器对象,可以与任意锁进行组合。
await():-->wait();
signal();-->notify();
signalAll();-->notifyAll();
3,实现机制对比:
旧方法:
class Object {
wait();
notify();
notifyAll();
}
class Demo extends Object {}
...
Demo d = new Demo();
synchronized(d){
d.wait();
}
一个锁只能实现一组wait,notify,notifyAll方法。
JDK1.5新特性:
interface Condition {
await();
signal();
signalAll();
}
Lock lock = new ReentrantLock();
Condition c1 = lock.newCondition();
Condition c2 = lock.newCondition();
可以创建多个Condition对象,实现多组Condition中的await,signal,signalAll。
4,14-4中的代码可以写为:
//创建一个锁对象
Lock lock = new ReentrantLock();
//通过已有的锁获取该锁上的监视器对象
Condition con = lock.newCondition();
public voidset(String name) {
//获取锁
lock.lock();
try {
while(true) {
try {
//调用监视器的await方法
con.awati();
}catch(InterrputedException e) {}
}
this.name = name + count;
count++;
System.out.println(Thread.currentThread().getName()+"生产者5.0"+this.name);
flag = true;
//调用监视器的signalAll方法
con.signalAll();
} finally {
//释放锁
lock.unlock();
}
}
14-7,解决方案
1,在14-4的程序中的Resource类改为。
import java.util.concurrent.locks.*;
class Resource {
private String name;
private int count = 1;
private boolean flag = false;
//创建锁对象
Lock lock = new ReentrantLock();
//通过已经有的锁获取两组监视器,一组监视生产者,一组监视消费者
Condition producer_con = lock.newCondition();
Condition consumer_con = lock.newCondition();
public void set(String name) {
lock.lock();
try {
while(true) {
try {
producer_con.await();
}catch(InterruputedException e) {}
}
this.name = na