一、多生产者多消费者
1.使用while判断标记,解决了线程获取执行权限后,是否要运行的问题。
因为使用if判断时,当线程被唤醒后因为已经判断过标记,线程就会继续执行,倘若此时已有生产的产品未被消费,线程仍然会生产产品。
2.notifyAll解决了,本方线程一定会唤醒对象线程。
仅使用notify唤醒单线程的时候,有可能会唤醒本方线程,因为此时的标记还未改变,而对方的线程仍在冻结,在进行判断的时候本方的线程 会因为标记的判断成功而陷入冻结,导致没有线程处于唤醒状态。进而导致死锁。
public class Resource { private String name; private int count = 1; private boolean flag = false; public synchronized void set(String name){ //生产操作 while(flag) try{this.wait();}catch(InterruptedException e){} this.name = name + count; count++; System.out.println(Thread.currentThread().getName()+"生产了。。。"+this.name); flag = true; this.notifyAll(); } public synchronized void out(){ //消费操作 while(!flag) try{this.wait();}catch(InterruptedException e){} System.out.println(Thread.currentThread().getName()+"消费了。。。"+this.name); flag = false; this.notifyAll(); } } public class Produce implements Runnable{ private Resource r; Produce(Resource r){ this.r = r; } public void run(){ while(true){ r.set("烤鸭"); //调用生产操作 } } } public class Consumer implements Runnable{ private Resource r; Consumer(Resource r){ this.r = r; } public void run(){ while(true) { r.out(); //调用消费操作 } } } public class ResourceDemo { public static void main(String[] args) { Resource r = new Resource(); Produce in = new Produce(r); Consumer out = new Consumer(r); Thread t0 = new Thread(in); Thread t1 = new Thread(in); Thread t2 = new Thread(out); Thread t3 = new Thread(out); t0.start(); t1.start(); t2.start(); t3.start(); } }
使用while语句以及notifyAll语句虽然解决了死锁的问题,但是在唤醒全部线程的同时,判断己方的线程会导致资源的浪费,JDK1.5版本以后的Lock接口解决了这个问题。
二、Lock接口
Lock提供了比使用synchronize接口更广泛灵活的操作,被用来替代synchronize。
Condition接口替代了Object监视器方法的使用。
新的接口Condition将Object监视器方法(wait,notify,notifyAll)分解为既然不同的对象
之前使用synchronize接口时,因为生产者与消费者之间使用同一把锁来保持同步,所以只有一个监视器,却要同时监视生产者和消费者两者的线程。
使用新的Lock接口时,Condition接口可以将多个监视器与同一把锁进行绑定,
import java.util.concurrent.locks.*; public class Resource { private String name; private int count = 1; private boolean flag = false; Lock lock = new ReentrantLock(); Condition pro_con = lock.newCondition(); //通过已有的锁获取两组锁上的监视器对象,一组监视生产者,一组监视消费者 Condition con_con = lock.newCondition(); public void set(String name){ //生产操作 lock.lock(); //创建一个锁 try { while(flag) //消费者等待 try {con_con.await();}catch(InterruptedException e){} this.name = name + count; count++; System.out.println(Thread.currentThread().getName() + "生产了。。。" + this.name); flag = true; pro_con.signal(); //生产者唤醒 } finally { lock.unlock(); } } public void out() { //消费操作 lock.lock(); try { while(!flag) //生产者等待 try {pro_con.await();} catch (InterruptedException e) {} System.out.println(Thread.currentThread().getName() + "消费了。。。" + this.name); flag = false; con_con.signal(); //消费者唤醒 } finally { lock.unlock(); } } } public class Person { private int age; Person(int age){ this.age = age; } public boolean equals(Object obj){ Person p = (Person)obj; return this.age == p.age; } } public class Consumer implements Runnable{ private Resource r; Consumer(Resource r){ this.r = r; } public void run(){ while(true) { r.out(); } } } public class ResourceDemo { public static void main(String[] args) { Resource r = new Resource(); Produce in = new Produce(r); Consumer out = new Consumer(r); Thread t0 = new Thread(in); Thread t1 = new Thread(in); Thread t2 = new Thread(out); Thread t3 = new Thread(out); t0.start(); t1.start(); t2.start(); t3.start(); } }
Lock接口的出现替代了同步代码块或者同步函数,将同步的隐式操作变成显示操作,
同时更为灵活,可以一把锁加上多组监视器。
JAVA学习(等待唤醒常考例子:生产者消费者,Lock接口,Condition接口)
原文:https://www.cnblogs.com/gmangofight/p/14607478.html