(52)多个生产者多个消费者出现的问题,解决方式synchronized方法和Lock方法
public class Resoure {
private String name;
private int count =1;
private boolean flag=false;
public synchronized void set(String name) {
if(flag) {
try {
this.wait();
} catch (Exception e) {
}
}
this.name=name+"----"+count++;
System.out.println(Thread.currentThread().getName()+"生产者"+this.name);
flag=true;
this.notify();
}
public synchronized void out() {
if (!flag) {
try {
this.wait();
} catch (Exception e) { }
}
System.out.println(Thread.currentThread().getName()+"消费者"+this.name);
flag=false;
this.notify();
}
}
public class Producer implements Runnable {
private Resoure res;
Producer(Resoure res){
this.res=res;
}
public void run() {
while(true) {
res.set("+商品+");
}
}
}
public class Consumer implements Runnable {
private Resoure res;
Consumer(Resoure res){
this.res=res;
}
public void run() {
while(true) {
res.out();
}
}
}
public class ProduceConsumerDemo {
public static void main(String[] args) {
Resoure r=new Resoure();
Producer pro=new Producer(r);
Consumer con=new Consumer(r);
Thread t1=new Thread(pro);
Thread t2=new Thread(con);
t1.start();
t2.start();
}
}
两个生产者两个消费者,这个程序会出现问题:
t1线程开启,flag=false,所以生产了一个数据,将flag置为true,唤醒该对象的其他线程,t1可能还有执行权,所以继续执行,flag=true,等待,放弃资格。
其他当前对象的t2,t3,t4都有可能抢到资源。假设t2抢到了资源,因为flag是个共享资源,所以flag为真,等待放弃资格。
t3线程获得资格,if判断失败,即有数据可消费,所以消费,flag=false
唤醒t1。(正常消费),t3可能还可以执行,但if判断失败,所以等待,放弃执行资格.
假设此时t4此时获得了执行权,因为if判断失败,等待,放弃执行资格。
就只剩t1有执行资格,T1有执行资格,所以生产一个数据,flag=true,唤醒该对象的其他线程,t1仍有执行权,if判断失败放弃资格。此时,它唤醒的是t2(才会出现两个生产打印,只消费第一个情况),t2获取资格,此时,只有t2可以获得执行权,所以应从刚才等待的下一条语句开始执行,所以又生产了一个数据。
生产两个数据,第一个生产的数据被覆盖,输出的就是第二个数据。
为什么会出现这种情况呢?
原因是t1生产完后,将flag=true,理应t2不能生产,但是t1生产完后,可以唤醒t2,就不再判断if(flag),直接生产.
修改方法:
让这种情况消失需要修改的地方是将if(flag)改为while(flag),即使醒了,因为while,也会判断flag.
但是呢,这样做,会造成全睡的情况,全部等待,刚才就是只有t2获得资格,但是flag判断失败,所以全没资格了。但不是死锁。
死锁是锁中有异锁。
继续修改:
唤醒所有,将this.notify(),改为this.notifyall(),这样t2,t3,t4全唤醒,t2中while判断失败,失去资格。t3,t4虽然会都醒,但是只能一个线程有执行权,假设t3获得执行权,while(!flag)假,消费数据,
然后flag=false,t4又没有执行条件了。
通过Lock对此程序进行修改和以上的修改,就是表达方式不同
import java.util.concurrent.locks.*;
public class Resoure {
private String name;
private int count =1;
private boolean flag=false;
private Lock lock=new ReentrantLock();
private Condition condition=lock.newCondition();
public void set(String name)throws InterruptedException {
lock.lock();
try {
while(flag)
condition.await();
this.name=name+"----"+count++;
System.out.println(Thread.currentThread().getName()+"生产者"+this.name);
flag=true;
condition.signalAll();
}
finally {
lock.unlock();
}
}
public void out()throws InterruptedException {
lock.lock();
try {
while (!flag)
condition.await();
System.out.println(Thread.currentThread().getName()+"消费者"+this.name);
flag=false;
condition.signalAll();
}
finally {
lock.unlock();
}
}
}
上面的方法无论是signalAll,还是notifyAll都是唤醒其他等待的,可以支持多个相关的 Condition 对象,所以生产者可以唤醒消费者,互相唤醒。
**jdk1.5中提供了多线程升级解决方案。
将同步synchronized替换成现实Lock操作。
将Object中的wait,notify,notifyAll替换成Condition对象,该对象可以Lock锁,进行获取。**
import java.util.concurrent.locks.*;
public class Resoure {
private String name;
private int count =1;
private boolean flag=false;
private Lock lock=new ReentrantLock();
private Condition condition_pro=lock.newCondition();
private Condition condition_con=lock.newCondition();
public void set(String name)throws InterruptedException {
lock.lock();
try {
while(flag)
condition_pro.await();
this.name=name+"----"+count++;
System.out.println(Thread.currentThread().getName()+"生产者"+this.name);
flag=true;
condition_con.signalAll();
}
finally {
lock.unlock();
}
}
public void out()throws InterruptedException {
lock.lock();
try {
while (!flag)
condition_con.await();
System.out.println(Thread.currentThread().getName()+"消费者"+this.name);
flag=false;
condition_pro.signalAll();
}
finally {
lock.unlock();
}
}
}