生产者(厨师做面条)消费者(吃货吃面条)模式synchronized 法和Lock+Condition法(不使用集合),代码详解

1. synchronized + wait + notifyAll

  • synchronized:包裹着同步代码块,代码块执行完释放锁
  • wait:线程挂起,失去锁
  • notifyAll:唤醒同个对象的wait,此时wait所在线程处于争抢线程执行权的状态,如果抢到了,让wait所在的线程继续往下执行(wait代码的下一行代码开始)
  • 为什么厨师(或者吃货)执行完synchronized同步代码块后(厨师和吃货的锁已经开启,都蓄势待发抢执行权)一定会继续执行第二次,而不会被已经唤醒的吃货(或者厨师)抢了执行权?因为厨师(或者吃货)处于while循环中,执行得更快。
    以下代码证明上述四点:

2. Lock+Condition+await+signalAll

==========================================================

1. synchronized + wait + notifyAll

测试类

public class ThreadDemo {
    public static void main(String[] args) {
        //创建线程的对象
        Cook c = new Cook();
        Foodie f = new Foodie();

        //给线程设置名字
        c.setName("厨师");
        f.setName("吃货");

        //开启线程
        c.start();
        f.start();
    }
}

厨师

public class Cook extends Thread{

    @Override
    public void run() {
        int i = 0;  //记录厨师第几次进锁
        while (true){//线程c
            synchronized (Desk.lock){
                i++;                    //比如:厨师第1次进锁
                System.out.println(getName() +"第"+ i+"次进锁");
                if(Desk.count == 0){
                    break;
                }else{
                    //判断桌子上是否有食物
                    if(Desk.foodFlag == 1){
                        System.out.println("cook waiting");
                        //如果有,就等待
                        try {
                            Desk.lock.wait();
                            //wait执行后下面代码不会执行,直到被notify,然后获得执行权后,
                            //才会在控制台打印厨师被唤醒了
                            System.out.println(getName()+"被唤醒了");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }else{
                        //如果没有,就制作食物
                        System.out.println("厨师做了一碗面条");
                        //修改桌子上的食物状态
                        Desk.foodFlag = 1;
                        //叫醒等待的消费者开吃
                        Desk.lock.notifyAll();
                        try {
                        //尽管notifyAll唤醒了wait中的吃货,但是厨师还
                        //是占用着锁,即使sleep了1秒也不会被蓄势待发的吃货抢到执行权
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}

吃货

public class Foodie extends Thread{

    @Override
    public void run() {
        int i = 0;  //记录吃货第几次进锁

        while(true){//线程f
            synchronized (Desk.lock){
                i++;                       //比如:吃货第1次进锁
                System.out.println(getName() +"第"+ i+"次进锁");
                if(Desk.count == 0){
                    break;
                }else{
                    //先判断桌子上是否有面条
                    if(Desk.foodFlag == 0){
                        System.out.println("foodie waiting");
                        //如果没有,就等待
                        try {
                            Desk.lock.wait();//让当前线程跟锁进行绑定
                            //wait执行后下面代码不会执行,直到被notify,然后获得执行权后,
                            //才会在控制台打印吃货被唤醒了
                            System.out.println(getName()+"被唤醒了");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }else{
                        //把吃的总数-1
                        Desk.count--;
                        //如果有,就开吃
                        System.out.println("吃货在吃面条,还能再吃" + Desk.count + "碗!!!");
                        //吃完之后,唤醒厨师继续做
                        Desk.lock.notifyAll();
                        //修改桌子的状态
                        Desk.foodFlag = 0;
                        try {
                        //尽管notifyAll唤醒了wait中的厨师,但是吃货还
                        //是占用着锁,即使sleep了1秒也不会被蓄势待发的厨师抢到执行权
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}

桌子 为了方便设置吃货能吃的面条为count=3

public class Desk {
    //是否有面条  0:没有面条  1:有面条
    public static int foodFlag = 0;

    //总个数
    public static int count = 3;

    //锁对象
    public static Object lock = new Object();
}

执行ThreadDemo 测试类,结果如下:
为什么厨师先执行?那是因为测试类中
//开启线程
c.start();
f.start();
厨师先start

厨师第1次进锁
厨师做了一碗面条 	//此时厨师线程的synchronized同步代码块已经执行完毕!
				//锁已经开启,此时厨师和吃货是同等概率争取执行权的,
				//但是由于厨师处于while(true)死循环中,会比吃货更快
				//抢到执行权,所以厨师第2次进锁。下面代码会证明。
				//厨师唤醒吃货,第一次执行吃货还没有waiting
厨师第2次进锁
cook waiting  	//厨师释放锁,处于waiting中,
吃货第1次进锁	
吃货在吃面条,还能再吃1碗!!!	//吃完面条,同时唤醒厨师,厨师处于争抢执行权状态,
							//但是抢不过现在的吃货,因为吃货处于死循环中,执行更快。
吃货第2次进锁	//同理,吃货虽然执行完了同步代码块,但是处于死循环中
foodie waiting	//执行到wait代码,让吃货处于waiting状态
厨师被唤醒了		//上一次厨师waiting被唤醒后,抢到了执行权继续执行下一行代码
厨师第3次进锁
厨师做了一碗面条
厨师第4次进锁
cook waiting
吃货被唤醒了
吃货第3次进锁
吃货在吃面条,还能再吃0碗!!!
吃货第4次进锁
厨师被唤醒了
厨师第5次进锁

Process finished with exit code 0

证明死循环的线程更快,直接在厨师的同步代码块前增加一一行代码,或者sleep,延迟一下时间,看吃货有没有抢先一步。
厨师增加一行代码

public class Cook extends Thread{

    @Override
    public void run() {
        int i = 0;
        System.out.println(getName()+"蓄势待发,想要进锁");
        while (true){//线程c
            synchronized (Desk.lock){
                i++;
                System.out.println(getName() +"第"+ i+"次进锁");
                if(Desk.count == 0){
                    break;
                }else{
                    //判断桌子上是否有食物
                    if(Desk.foodFlag == 1){
                        System.out.println("cook waiting");
                        //如果有,就等待
                        try {
                            Desk.lock.wait();
                            System.out.println(getName()+"被唤醒了");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }else{
                        //如果没有,就制作食物
                        System.out.println("厨师做了一碗面条");
                        //修改桌子上的食物状态
                        Desk.foodFlag = 1;
                        //叫醒等待的消费者开吃
                        Desk.lock.notifyAll();
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}

运行结果:

厨师蓄势待发,想要进锁	测试类中,厨师先start
吃货第1次进锁	//由于打印了语句,厨师耽误了时间,给吃货抢先一步获得了执行权
foodie waiting
厨师第1次进锁
厨师做了一碗面条
厨师蓄势待发,想要进锁	//厨师蓄势待发,想进入第二次循环,却因为打印语句
					//浪费了时间,被吃货抢到了执行权,如下
吃货被唤醒了
吃货第2次进锁
吃货在吃面条,还能再吃1碗!!!
吃货第3次进锁
foodie waiting
厨师第2次进锁
厨师做了一碗面条
吃货被唤醒了
厨师蓄势待发,想要进锁
吃货第4次进锁
吃货在吃面条,还能再吃0碗!!!
吃货第5次进锁
厨师第3次进锁

Process finished with exit code 0

=========================================================

2. Lock+Condition+await+signalAll

测试类

package com.cfj.lockstest;

public class ThreadDemo {
    public static void main(String[] args) {
        //创建线程的对象
        Cook c = new Cook();
        Foodie f = new Foodie();

        //给线程设置名字
        c.setName("厨师");
        f.setName("吃货");

        //开启线程
        c.start();
        f.start();
    }
}

厨师

package com.cfj.lockstest;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Cook extends Thread {

    @Override
    public void run() {
        int i = 0;

        while (true) {//线程c
            System.out.println(getName() + "蓄势待发,想要进锁");
            //synchronized (Desk.lock){
            //lock.lock();
            Desk.lock.lock();
            try {
                i++;
                System.out.println(getName() + "第" + i + "次进锁");
                if (Desk.count == 0) {
                    break;
                } else {
                    //判断桌子上是否有食物
                    if (Desk.foodFlag == 1) {
                        System.out.println("cook waiting");
                        //如果有,就等待
                        try {
                            //Desk.lock.wait();
                            Desk.condition.await();
                            System.out.println(getName() + "被唤醒了");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    } else {
                        //如果没有,就制作食物
                        System.out.println("厨师做了一碗面条");
                        //修改桌子上的食物状态
                        Desk.foodFlag = 1;
                        //叫醒等待的消费者开吃
                        //Desk.lock.notifyAll();
                        Desk.condition.signalAll();
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                Desk.lock.unlock();
            }
//            }
        }
    }
}

吃货

package com.cfj.lockstest;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Foodie extends Thread {
    static Lock lock = new ReentrantLock();

    @Override
    public void run() {
        /*
         * 1. 循环
         * 2. 同步代码块
         * 3. 判断共享数据是否到了末尾(到了末尾)
         * 4. 判断共享数据是否到了末尾(没有到末尾,执行核心逻辑)
         * */
        int i = 0;

        while (true) {//线程f
            //synchronized (Desk.lock){
            //lock.lock();
            Desk.lock.lock();
            try {
                i++;
                System.out.println(getName() + "第" + i + "次进锁");
                if (Desk.count == 0) {
                    break;
                } else {
                    //先判断桌子上是否有面条
                    if (Desk.foodFlag == 0) {
                        System.out.println("foodie waiting");
                        //如果没有,就等待
                        try {
                            //Desk.lock.wait();//让当前线程跟锁进行绑定
                            Desk.condition.await();
                            System.out.println(getName() + "被唤醒了");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    } else {
                        //把吃的总数-1
                        Desk.count--;
                        //如果有,就开吃
                        System.out.println("吃货在吃面条,还能再吃" + Desk.count + "碗!!!");
                        //吃完之后,唤醒厨师继续做
                        //Desk.lock.notifyAll();
                        Desk.condition.signalAll();
                        //修改桌子的状态
                        Desk.foodFlag = 0;
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                Desk.lock.unlock();
            }
//            }
        }
    }
}

桌子

package com.cfj.lockstest;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Desk {
    //是否有面条  0:没有面条  1:有面条
    public static int foodFlag = 0;

    //总个数
    public static int count = 2;

    //锁对象
//    public static Object lock = new Object();
    public static Lock lock = new ReentrantLock();
    public static Condition condition = lock.newCondition();
}

运行结果:

厨师蓄势待发,想要进锁
吃货第1次进锁
foodie waiting
厨师第1次进锁
厨师做了一碗面条
厨师蓄势待发,想要进锁
吃货被唤醒了
吃货第2次进锁
吃货在吃面条,还能再吃1碗!!!
吃货第3次进锁
foodie waiting
厨师第2次进锁
厨师做了一碗面条
厨师蓄势待发,想要进锁
吃货被唤醒了
吃货第4次进锁
吃货在吃面条,还能再吃0碗!!!
吃货第5次进锁
厨师第3次进锁
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值