一 案例目地
掌握控制线程的执行次序,实现按照要求的线程执行
二 案例要求:
生产者:
- 判断 “桌子上” 是否有 “食品” ,如果有就等待,如果没有才生产
- 把 “食品” 放在 “桌子上”。
- 叫醒等待的消费者
- 生产者存在生产数量的限制
消费者:
- 判断 “桌子上” 是否有 “食品” ,如果没有就等待,如果有就 “吃” 掉 “食品”
- 吃完后,叫醒等待的生产者,继续生产 “食品”
三 方法提供
1 void wait(); 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或者 notifyAll()方法 2 void notify(); 唤醒正在等待对象监视器的单个线程 3 void notifyAll(); 唤醒正在等待对象监视器的所有线程
- 注:使用什么对象作为锁,那么就必须用这个对象去调用等待和唤醒方法,notify()随机唤醒这把锁上等待的一个线程,notifyAll()唤醒这把锁上的所有线程
四 代码演示
- 测试类:
public class Test { public static void main(String[] args) { Producer p = new Producer(); Consumer c = new Consumer(); p.start(); c.start(); } }
- 生产者类:
//创建生产者类 public class Producer extends Thread{ public void run() { //因为操作不止一次,使用while循环 while (true){ //共享数据,使用同步代码块 synchronized (Desk.lock){ //判断是否达到,生产者上线 if (Desk.count == 0){ break; }else { //判断桌上是否有“食品” if(Desk.flag){ //如果有,就等待 try { Desk.lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } }else { //如果没有,进行生产 System.out.println("生产者正在生产"); //表示有食品了 Desk.flag = true; //叫醒消费者 Desk.lock.notifyAll(); //生产者上线数减一 Desk.count--; } } } } } }
- 消费者类:
//创建消费者类 public class Consumer extends Thread{ public void run() { //因为操作不止一次,使用while循环 while (true){ //共享数据,使用同步代码块 synchronized (Desk.lock){ //判断是否达到,生产者上线 if (Desk.count == 0){ break; }else { //判断桌上是否有“食品” if(Desk.flag){ //如果有 System.out.println("消费者正在吃掉食品"); //表示没有食品了 Desk.flag = false; //叫醒生产者 Desk.lock.notifyAll(); }else { //如果没有,进行等待 //使用什么对象作为锁,那么就必须用这个对象去调用等待与唤醒方法 try { Desk.lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } } }
- “桌子类”:
//创建桌子类 public class Desk { //定义标记,如果为true,表示桌子上有“食品”,如果false表示无 public static boolean flag = false; //定义,生产的生产上线 public static int count = 10; //锁对象,在此次定义,使用final,目地:保证消费者与生产者锁对象唯一 public static final Object lock = new Object(); }
- 效果演示:实现了,对生产者与消费者的执行次序控制
五 线程写法小结
- while(true)死循环
- synchronized 锁,锁对象要唯一
- 判断,共享数据是否结束,如果结束就跳出循环
- 如果没有结束,执行题目逻辑要求
六 代码优化
为了体现面向对象编程的特性,对代码进行优化
- 桌子类进行封装:
//创建桌子类 public class Desk { //定义标记,如果为true,表示桌子上有“食品”,如果false表示无 //public static boolean flag = false; private boolean flag = false; //定义,生产的生产上线 //public static int count = 10; private int count = 10; //锁对象,在此次定义,使用final,目地:保证消费者与生产者锁对象唯一 //public static final Object lock = new Object(); private final Object lock = new Object(); //空参构造 public Desk() { } //全参构造 public Desk(boolean flag, int count) { this.flag = flag; this.count = count; } //提供get,set方法 //boolean的get方法叫做is... public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } //lock因为由final修饰,所以没有set方法 public Object getLock() { return lock; } //toString()方法重写 public String toString() { return "Desk{" + "flag=" + flag + ", count=" + count + ", lock=" + lock + '}'; } }
- 测试类:
public class Test { public static void main(String[] args) { //在测试类中,创建esk对象D,防止Producer与Consumer的共享数据不同 Desk desk = new Desk(); //提供分别的Desk对象构造方法 Producer p = new Producer(desk); Consumer c = new Consumer(desk); p.start(); c.start(); } }
- 生产者类:
//创建生产者类 public class Producer extends Thread{ private Desk desk; public Producer(Desk desk) { this.desk = desk; } public void run() { //因为操作不止一次,使用while循环 while (true){ //共享数据,使用同步代码块 synchronized (desk.getLock()){ //判断是否达到,生产者上线 if (desk.getCount() == 0){ break; }else { //判断桌上是否有“食品” if(desk.isFlag()){ //如果有,就等待 try { desk.getLock().wait(); } catch (InterruptedException e) { e.printStackTrace(); } }else { //如果没有,进行生产 System.out.println("生产者正在生产"); //表示有食品了 desk.setFlag(true); //叫醒消费者 desk.getLock().notifyAll(); //生产者上线数减一 desk.setCount(desk.getCount() - 1); } } } } } }
- 消费者类:
//创建消费者类 public class Consumer extends Thread{ private Desk desk; public Consumer(Desk desk) { this.desk = desk; } public void run() { //因为操作不止一次,使用while循环 while (true){ //共享数据,使用同步代码块 synchronized (desk.getLock()){ //判断是否达到,生产者上线 if (desk.getCount() == 0){ break; }else { //判断桌上是否有“食品” if(desk.isFlag()){ //如果有 System.out.println("消费者正在吃掉食品"); //表示没有食品了 desk.setFlag(false); //叫醒生产者 desk.getLock().notifyAll(); }else { //如果没有,进行等待 //使用什么对象作为锁,那么就必须用这个对象去调用等待与唤醒方法 try { desk.getLock().wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } } }