• 【从入门到起飞】JavaSE—多线程(2)(lock锁,死锁,等待唤醒机制)



    在这里插入图片描述

    比如下面这一段代码
    在这里插入图片描述

    我们在上一篇文章中讲过,进程进入synchroized后,其他进程不能进入,每次只允许执行一个进程
    那么我们要想自定义运行过程,比如手动打开或者手动关闭,那么就应该使用lock锁

    🌺lock锁

    ⭐获得锁

    void lock();
    
    • 1

    ⭐释放锁

    void unlock();
    
    • 1

    ✨注意

    lock是接口不能直接实例化(即不能创建对象),可以采用它的实现类ReentrantLock来实例化

    构造方法

    Lock lock=new ReentrantLock();
    
    • 1

    🏳️‍🌈代码实现

    MyRunnable.java

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class MyRunnable implements Runnable {
        static int ticket = 0;
        static Lock lock = new ReentrantLock();
    
        @Override
        public void run() {
            while (true) {
                lock.lock();
    
                try {
                    if (ticket == 100) {
                        break;
                    } else {
                        Thread.sleep(10);
                        ticket++;
                        System.out.println(Thread.currentThread().getName() + "在卖第" + ticket + "张票!!!");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
    
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    ThreadDemo.java

    public class ThreadDemo {
        public static void main(String[] args) {
            MyRunnable mr=new MyRunnable();
    
            Thread t1=new Thread(mr);
            Thread t2=new Thread(mr);
            Thread t3=new Thread(mr);
    
            t1.setName("窗口1");
            t2.setName("窗口2");
            t3.setName("窗口3");
    
            t1.start();
            t2.start();
            t3.start();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在这里插入图片描述

    🎈细节

    在这里插入图片描述

    我们发现,虽然已经到了100张票,但是程序没有停止运行,这是为什么呢

    在这里插入图片描述
    线程一break了,但是没有运行unlock,导致二,三线程不能向下执行,所以程序不会停止运行

    改进方法

    在这里插入图片描述

    加上finally就行了,因为程序一定会执行finally中的代码

    🌺死锁

    比如 线程A 等待 线程B 释放锁,线程B 等待 线程A 释放锁,这样子二者就会卡死,这就是死锁

    ⭐解决方法

    就是以后写代码的时候,尽量不要把2个锁嵌套起来

    🎄等待唤醒机制

    我们可以这样子来形容这个机制
    比如顾客(消费者)和厨师(生产者)之间的关系

    在这里插入图片描述

    ⭐代码实现

    在这里插入图片描述

    🤖Foodie.java

    public class Foodie extends Thread{
    
        @Override
        public void run() {
            /*
            * 1. 循环
            * 2. 同步代码块
            * 3. 判断共享数据是否到了末尾(到了末尾)
            * 4. 判断共享数据是否到了末尾(没有到末尾,执行核心逻辑)
            * */
    
            while(true){
                synchronized (Desk.lock){
                    if(Desk.count == 0){
                        break;
                    }else{
                        //先判断桌子上是否有面条
                        if(Desk.foodFlag == 0){
                            //如果没有,就等待
                            try {
                                Desk.lock.wait();//让当前线程跟锁进行绑定
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }else{
                            //把吃的总数-1
                            Desk.count--;
                            //如果有,就开吃
                            System.out.println("吃货在吃面条,还能再吃" + Desk.count + "碗!!!");
                            //吃完之后,唤醒厨师继续做
                            Desk.lock.notifyAll();
                            //修改桌子的状态
                            Desk.foodFlag = 0;
                        }
                    }
                }
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    🤖Cook.java

    public class Cook extends Thread{
    
        @Override
        public void run() {
            /*
             * 1. 循环
             * 2. 同步代码块
             * 3. 判断共享数据是否到了末尾(到了末尾)
             * 4. 判断共享数据是否到了末尾(没有到末尾,执行核心逻辑)
             * */
    
            while (true){
                synchronized (Desk.lock){
                    if(Desk.count == 0){
                        break;
                    }else{
                        //判断桌子上是否有食物
                        if(Desk.foodFlag == 1){
                            //如果有,就等待
                            try {
                                Desk.lock.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }else{
                            //如果没有,就制作食物
                            System.out.println("厨师做了一碗面条");
                            //修改桌子上的食物状态
                            Desk.foodFlag = 1;
                            //叫醒等待的消费者开吃
                            Desk.lock.notifyAll();
                        }
                    }
                }
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    🤖Desk.java

    public class Desk {
    
        /*
        * 作用:控制生产者和消费者的执行
        *
        * */
    
        //是否有面条  0:没有面条  1:有面条
        public static int foodFlag = 0;
    
        //总个数
        public static int count = 10;
    
        //锁对象
        public static Object lock = new Object();
    
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    🤖ThreadDemo.java

    
    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();
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    🎈注意

    1.继承Thread类的主要目的是为了能够重写Thread类中的run()方法
    2.为了可以共用同一个资源,Desk类的对象中要加上static
    3.锁对象是唯一的

    🛸使用阻塞队列实现等待唤醒机制

    创建阻塞队列的对象
    ArrayBlockingQueue < String > queue = new ArrayBlockingQueue<>(1);

    🤖Cook.java

    import java.util.concurrent.ArrayBlockingQueue;
    
    public class Cook extends Thread{
    
        ArrayBlockingQueue<String> queue;
    
        public Cook(ArrayBlockingQueue<String> queue) {
            this.queue = queue;
        }
    
        @Override
        public void run() {
            while(true){
                //不断的把面条放到阻塞队列当中
                try {
                    queue.put("面条");
                    System.out.println("厨师放了一碗面条");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    🤖Foodie.java

    import java.util.concurrent.ArrayBlockingQueue;
    
    public class  Foodie extends Thread{
    
        ArrayBlockingQueue<String> queue;
    
        public Foodie(ArrayBlockingQueue<String> queue) {
            this.queue = queue;
        }
    
    
        @Override
        public void run() {
            while(true){
                    //不断从阻塞队列中获取面条
                    try {
                        String food = queue.take();
                        System.out.println(food);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    🤖ThreadDemo.java

    import java.util.concurrent.ArrayBlockingQueue;
    
    public class ThreadDemo {
        public static void main(String[] args) {
           /*
           *
           *    需求:利用阻塞队列完成生产者和消费者(等待唤醒机制)的代码
           *    细节:
           *           生产者和消费者必须使用同一个阻塞队列
           *
           * */
    
            //1.创建阻塞队列的对象
            ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);
    
    
            //2.创建线程的对象,并把阻塞队列传递过去
            Cook c = new Cook(queue);
            Foodie f = new Foodie(queue);
    
            //3.开启线程
            c.start();
            f.start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    🍔线程的六种状态

    在这里插入图片描述

    如果大家有什么不明白的地方,请在评论区进行讨论

  • 相关阅读:
    《canvas》之第2章 直线图形
    【EI会议征稿】 2024年遥感、测绘与图像处理国际学术会议(RSMIP2024)
    Nacos源码详解
    SpringSecurity系列——安全Http响应头day8-2(源于官网5.7.2版本)
    图像识别算法汇总
    XILINX 7系列XDMA使用_IP核介绍以及工程搭建
    Git rebase 用法说明
    我做了一个可视化生成ECharts代码的应用,再也不用去看复杂的文档了...(附项目源码)
    力扣2860 补9.20
    java面试题含答案总结七
  • 原文地址:https://blog.csdn.net/m0_72853403/article/details/134550801