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

文章目录

  • 🌺lock锁
    • ⭐获得锁
    • ⭐释放锁
    • ✨注意
    • 🏳️‍🌈代码实现
      • 🎈细节
  • 🌺死锁
    • ⭐解决方法
  • 🎄等待唤醒机制
    • ⭐代码实现
      • 🎈注意
    • 🛸使用阻塞队列实现等待唤醒机制
  • 🍔线程的六种状态

在这里插入图片描述

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

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

🌺lock锁

⭐获得锁

void lock();

⭐释放锁

void unlock();

✨注意

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

构造方法

Lock lock=new ReentrantLock();

🏳️‍🌈代码实现

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();@Overridepublic 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();}}}
}

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();}
}

在这里插入图片描述

🎈细节

在这里插入图片描述

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

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

改进方法

在这里插入图片描述

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

🌺死锁

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

⭐解决方法

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

🎄等待唤醒机制

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

在这里插入图片描述

⭐代码实现

在这里插入图片描述

🤖Foodie.java

public class Foodie extends Thread{@Overridepublic 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{//把吃的总数-1Desk.count--;//如果有,就开吃System.out.println("吃货在吃面条,还能再吃" + Desk.count + "碗!!!");//吃完之后,唤醒厨师继续做Desk.lock.notifyAll();//修改桌子的状态Desk.foodFlag = 0;}}}}}
}

🤖Cook.java

public class Cook extends Thread{@Overridepublic 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();}}}}}
}

🤖Desk.java

public class Desk {/** 作用:控制生产者和消费者的执行** *///是否有面条  0:没有面条  1:有面条public static int foodFlag = 0;//总个数public static int count = 10;//锁对象public static Object lock = new Object();}

🤖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.继承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;}@Overridepublic void run() {while(true){//不断的把面条放到阻塞队列当中try {queue.put("面条");System.out.println("厨师放了一碗面条");} catch (InterruptedException e) {e.printStackTrace();}}}
}

🤖Foodie.java

import java.util.concurrent.ArrayBlockingQueue;public class  Foodie extends Thread{ArrayBlockingQueue<String> queue;public Foodie(ArrayBlockingQueue<String> queue) {this.queue = queue;}@Overridepublic void run() {while(true){//不断从阻塞队列中获取面条try {String food = queue.take();System.out.println(food);} catch (InterruptedException e) {e.printStackTrace();}}}
}

🤖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();}
}

🍔线程的六种状态

在这里插入图片描述

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/208953.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java计算时间差,距结束还有几天几小时几分钟

文章目录 1、写法2、备份3、LocalDate、LocalDateTime、Date、String互转 1、写法 //静态方法&#xff0c;传入年月日时分秒 LocalDateTime startTime LocalDateTime.of(2023, 11, 22, 15, 09, 59); LocalDateTime endTime LocalDateTime.of(2023, 11, 30, 0, 0, 0); //计算…

738. Monotone Increasing Digits 968. Binary Tree Cameras

738. Monotone Increasing Digits An integer has monotone increasing digits单调递增数字 if and only if each pair of adjacent digits x and y satisfy x < y. Given an integer n, return the largest number that is less than or equal to n with monotone increa…

Go 异常处理流程

在 Go 语言中&#xff0c;panic、recover 和 defer 是用于处理异常情况的关键字。它们通常一起使用来实现对程序错误的处理和恢复。 1. defer 语句 defer 用于在函数返回之前执行一段代码。被 defer 修饰的语句或函数会在包含 defer 的函数执行完毕后执行。defer 常用于资源清…

国内怎么投资黄金,炒黄金有哪些好方法?

随着我国综合实力的不断强大&#xff0c;投资市场的发展也日臻完善&#xff0c;现已成为了国际黄金市场的重要组成部分&#xff0c;人们想要精准判断金市走向&#xff0c;就离不开对我国经济等信息的仔细分析。而想要有效提升盈利概率&#xff0c;人们还需要掌握国内黄金投资的…

【计算机基础】通过插件plantuml,实现在VScode里面绘制状态机

&#x1f4e2;&#xff1a;如果你也对机器人、人工智能感兴趣&#xff0c;看来我们志同道合✨ &#x1f4e2;&#xff1a;不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 &#x1f4e2;&#xff1a;文章若有幸对你有帮助&#xff0c;可点赞 &#x1f44d;…

掌握 AI 和 NLP:深入研究 Python — 情感分析、NER 等

一、说明 我们见证了 BERT 等预训练模型在情感分析方面的强大功能,使我们能够破译隐藏在文本数据中的情感。通过 SpaCy,我们探索了命名实体识别的迷人世界,揭开了隐藏在非结构化文本中的秘密。 二、问题陈述 命名实体识别(NER)是自然语言处理中的一项关键…

大模型变身双面人:虚假新闻制造机VS假新闻鉴别大师!

大家是怎样看待大型语言模型生成信息的可靠性呢&#xff1f; 尽管大语言模型生成的内容“像模像样”&#xff0c;但这些模型偶尔的失误揭示了一个关键问题&#xff1a;它们生成的内容并不总是真实可靠的。 那么&#xff0c;这种“不保真”特性能否被用来制造虚假信息呢&#x…

SQLite3

数据库简介 常用的数据库 大型数据库&#xff1a;Oracle 中型数据库&#xff1a;Server 是微软开发的数据库产品&#xff0c;主要支持 windows 平台。 小型数据库&#xff1a;mySQL 是一个小型关系型数据库管理系统&#xff0c;开放源码 。(嵌入式不需要存储太多数据。) SQL…

天猫商品详情数据接口(Tmall.item_get)

天猫商品详情数据接口是天猫开放平台提供的一种API接口&#xff0c;通过调用该接口&#xff0c;可以获取天猫平台上的商品详情信息。该接口的主要作用是帮助开发者获取商品的详细数据&#xff0c;包括商品的ID、标题、价格、库存量、图片等信息&#xff0c;从而更好地了解和分析…

【数据结构】链表中二级指针的应用

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 (注:为方便演示本篇使用的x86系统,因此指针的大小为4个字节) 目录 &#x1f4cc;形参的改变不影响实参! 1.调用函数更改整型时传值调用与传址调用的区别 &#x1f38f;传值…

C语言——结构体的应用

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 路还在继续&#xff0c;梦还在期…

Vue 重写push和replace方法,解决:Avoided redundant navigation to current location

当我们使用编程式路由导航跳转路径时&#xff0c;如果我们两次携带同样的参数进行跳转&#xff0c;会进行页面报错&#xff1a; 那产生这个问题的原因是什么呢&#xff1f; 我们接收并输出调用push方法返回的结果&#xff1a; 会发现这是一个Promise对象 我们都知道&#xff…