当前位置: 首页 > news >正文

【Java开发】JUC基础 04:Synchronized、死锁、Lock锁

1 概念介绍

  • 并发同一个对象被多个线程同时操作

📌 线程同步

  • 现实生活中,我们会遇到“同一个资源,多个人都想使用”的问题,比如,食堂排队打饭,每个人都想吃饭,最天然的解决办法就是,排队,一个个来。

  • 处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象这时候我们就需要线程同步,线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用。

📌 队列和锁

  • 队列:队列等同于对象的等待池,各线程于队列中进行等待;

  • :保证队列的安全,保证同一时间只有一个线程操作该对象。

📌 Synchronized 方法/同步方法

Sybchronized 机制控制对对象的访问,每个对象对应一把锁,每个 synchronized 方法都必须获取调用方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,知道方法返回才释放锁,后面被阻塞的线程才能获得锁,继续执行;

存在以下问题:

  1. 一个线程持有锁会导致其他所有需要此锁的线程挂起;

  1. 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题;

  1. 如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能问题。

2 线程不安全及解决

2.1 同步方法

📌 要点

由于我们可以通过private关键字来保证数据对象只能被方法访问,所以我们只需要针对方法提出一套机制,这套机制就是synchronized关键字,它包括两种用法:synchronized方法 和synchronized块.

  • 同步方法: public synchronized void method(int args)

  • 同步代码块:synchronized(obj){}

Obj 称之为同步监视器,obj 可以是任何对象,但是推荐使用共享资源作为同步监视器,同步方法中无需指定同步监视器,因为同步方法的同步监视器就是 this,这个对象本身;

同步监视器的执行过程:
第一个线程访问,锁定同步监视器,执行其中的代码
第二个线程访问,发现同步监视器被锁定,无法访问
第一个线程访问完毕,解锁同步监视器
第二个线程访问,发现同步监视器没有锁,然后锁定并访问;

2.1 多人购票

📌 不安全代码

public class UnsafeBuyTicket{public static void main(String[] args) {BuyTicket buyTicket = new BuyTicket();new Thread(buyTicket, "A").start();new Thread(buyTicket, "B").start();new Thread(buyTicket, "C").start();}
}class BuyTicket implements Runnable{//票数private int ticketNums= 10;//外部停止方式boolean flag = true;@Overridepublic void run() {while (flag){buy();}}private void buy(){//判断是否有票if (ticketNums <=0){flag = false;return;}System.out.println(Thread.currentThread().getName()+"买到了第"+ticketNums--+"张票;");}
}

控制台输出:

📢 可以看到输出混乱,C和A拿到了同一张票,甚至有时会出现负数,这就是线程不安全。

这是因为同一时刻,两个线程进行统一操作,内存控制不当会导致数据不一致

📌 同步方法

给buy方法加上了锁~

控制台输出:

📢 可以看到很有顺序。

2.2 银行取钱

📌 不安全代码

public class UnsafeBank {public static void main(String[] args) {Account account = new Account(100,"建设银行共同");new Thread(new Drawing(account, 50),"A").start();new Thread(new Drawing(account, 100),"B").start();}
}//1.创建账户信息
class Account {//余额int money;//卡名String name;public Account(int money,String name){this.money = money;this.name = name;}
}class Drawing implements Runnable{Account account;//取出的钱int drawingMoney;//现有的钱int nowMoney;public Drawing(Account account,  int drawingMoney){this.account = account;this.drawingMoney = drawingMoney;}@Overridepublic void run() {//判断有没有钱if (account.money - drawingMoney < 0){System.out.println(Thread.currentThread().getName()+"操作:"+account.name+"账户没有那么多钱!");return;}//延时,保证两个线程都能抵达这try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//卡内余额 = 余额 - 取出的钱account.money = account.money - drawingMoney;//现有的钱nowMoney = nowMoney + drawingMoney;System.out.println(Thread.currentThread().getName()+"操作:"+account.name+"账户余额为"+account.money);System.out.println(Thread.currentThread().getName()+"手里的钱为"+nowMoney);}
}

控制台输出:

📢 可以看到两个线程都取了钱,超出了限制条件(不够取的话不能取),线程不安全!

📌 同步代码块

因为同步方法默认是锁this对象,但是这里需要锁这个账户,因此同步代码块,记住锁的对象是变化的量即可。

控制台输出:

📢 上锁成功

2.3 ArrayList

📌 不安全代码

public class UnsafeArrayList {public static void main(String[] args) {ArrayList<String> arrayList = new ArrayList<>();for (int i = 0; i < 10000; i++) {new Thread(()-> arrayList.add(Thread.currentThread().getName())).start();}System.out.println(arrayList.size());}
}

控制台输出:

📢 与10000对不上,说明线程名称加到集合中去的时候有冲突,也是线程不安全。

📌 同步代码块

在lambda表达式中给ArrayList对象添加锁~

控制台输出:

📢 上锁成功

2.4 线程安全的集合

这是Juc里边的集合,自带线程安全功能,底层也是用了锁。

import java.util.concurrent.CopyOnWriteArrayList;public class JucList {public static void main(String[] args) {CopyOnWriteArrayList<String> list= new CopyOnWriteArrayList<>();for (int i = 0; i < 10000; i++) {new Thread(()->list.add(Thread.currentThread().getName())).start();}//增加延时,方式main线程先结束,不然输出不准确try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(list.size());}
}

3 死锁和Lock锁

📌 要点

多个线程各自占有一些共享资源,并且相互等待其他线程占用的资源才能运行,而导致两个或两个以上的线程都在等待对方释放资源,都停止执行的情形;某一个同步块同时拥有两个对象以上的锁,就有可能发生死锁

📌 产生死锁的必要条件

  1. 互斥条件:一个线程只能被一个人使用

  1. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放

  1. 不剥夺条件: 进程已获得的资源,在未使用完之前,不能剥夺;

  1. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源的关系

3.1 死锁

简单来说,就是A线程拿到一个口号,B线程拿到一个镜子,然后他们都想再拿到对方的东西,但是无法成功,因为口号和镜子都被加锁了,只有一个线程能用。

//死锁:多个线程互相抱着对方需要的资源,然后形成僵持
public class DeadLock {public static void main(String[] args) {Thread t1 = new Thread(new MakeUp(0,"A"));Thread t2 = new Thread(new MakeUp(1,"B"));t1.start();t2.start();}
}//口红
class Lipstick{
}//镜子
class Mirror{
}class MakeUp implements Runnable{//需要的资源只有一份static Lipstick lipstick = new Lipstick();static Mirror mirror = new Mirror();int choice;//选择String name;//使用人public MakeUp(int choice, String name) {this.choice = choice;this.name = name;}@Overridepublic void run() {try {makeup();} catch (InterruptedException e) {e.printStackTrace();}}//化妆,互相持有对方的锁,拿到对方的资源private void makeup() throws InterruptedException {if(choice==0){synchronized (lipstick){System.out.println(this.name+"获取口红的锁");Thread.sleep(1000);synchronized (mirror){//一秒钟后获得镜子System.out.println(this.name+"获取镜子的锁");}}}else{synchronized (mirror){System.out.println(this.name+"获取镜子的锁");Thread.sleep(1000);synchronized (lipstick){//一秒钟后获得口红System.out.println(this.name+"获取口红的锁");}}}}
}

控制台输出:

📢 程序无法结束,这就是死锁。

📌 解决死锁

把同步代码块(上锁的)分开,这样锁就能得到释放。

3.2 Lock锁

📌 要点

  • Java 提供了更强大的线程同步机制—通过显式定义同步锁对象来实现同步;同步锁使用 Lock 对象充当;

  • Lock 接口时控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对 Lock 对象加锁,线程开始访问共享资源之前应先获得 Lokc 对象;

  • ReentrantLock 类(可重用锁)实现了 Lock, 他拥有 synchronized 相同的并发性和内存语义,在实现线程安全的控制中,比较常用的时 ReentrantLock,可以显示枷锁释放锁;

📌 未加Lock锁时

public class TestLock {public static void main(String[] args) {BuyTicket buyTicket = new BuyTicket();new Thread(buyTicket).start();new Thread(buyTicket).start();new Thread(buyTicket).start();}
}class BuyTicket implements Runnable{int ticketNums=10;@Overridepublic void run() {while (true){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}if(ticketNums>0){System.out.println(Thread.currentThread().getName()+"买到了票"+ticketNums--);}else {break;}}}
}

控制台输出:

📌 加Lock锁后

需要先定义ReentrantLock,然后再try{}finally{}中分别加锁和解锁。

public class TestLock {public static void main(String[] args) {BuyTicket buyTicket = new BuyTicket();new Thread(buyTicket).start();new Thread(buyTicket).start();new Thread(buyTicket).start();}
}class BuyTicket implements Runnable{int ticketNums=10;//定义lock锁private final ReentrantLock lock = new ReentrantLock();@Overridepublic void run() {while (true){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}try {lock.lock();//加锁if(ticketNums>0){System.out.println(Thread.currentThread().getName()+"买到了票"+ticketNums--);}else {break;}} finally {lock.unlock();//解锁}}}
}

控制台输出:

3.3 synchronized和Lock对比

📌 要点

  • Lock是显式锁(手动开启和关闭锁,别忘记关闭锁)synchronized是隐式锁,出了作用域自动释放

  • Lock只有代码块锁,synchronized有代码块锁和方法锁

  • 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)

  • 优先使用顺序:Lock>同步代码块(已经进入了方法体,分配了相应资源)>同步方法(在方法体之外)

相关文章:

【Java开发】JUC基础 04:Synchronized、死锁、Lock锁

1 概念介绍并发&#xff1a;同一个对象被多个线程同时操作&#x1f4cc; 线程同步现实生活中&#xff0c;我们会遇到“同一个资源&#xff0c;多个人都想使用”的问题&#xff0c;比如&#xff0c;食堂排队打饭,每个人都想吃饭&#xff0c;最天然的解决办法就是&#xff0c;排队…...

离散数学---期末复习知识点

一、 数理逻辑 [复习知识点] 1、命题与联结词&#xff08;否定&#xffe2;、析取∨、合取∧、蕴涵→、等价↔&#xff09;&#xff0c;命题(非真既假的陈述句),复合命题(由简单命题通过联结词联结而成的命题) 2、命题公式与赋值&#xff08;成真、成假&#xff09;&#x…...

在线安装ESP32和ESP8266 Arduino开发环境

esp32和esp8266都是乐鑫科技开发的单片机产品&#xff0c;esp8266价格便宜开发板只需要十多块钱就可以买到&#xff0c;而esp32是esp8266的升级版本&#xff0c;比esp8266的功能和性能更强大&#xff0c;开发板价格大约二十多元就可以买到。 使用Arduino开发esp32和esp8266需要…...

【Python实战】激情澎湃,2023极品劲爆舞曲震撼全场,爬虫一键采集DJ大串烧,一曲醉人女声DJ舞曲,人人都听醉~(排行榜采集,妙啊~)

导语 哈喽&#xff01;大家好。我是木木子吖~今天给大家带来爬虫的内容哈。 所有文章完整的素材源码都在&#x1f447;&#x1f447; 粉丝白嫖源码福利&#xff0c;请移步至CSDN社区或文末公众hao即可免费。 今天教大家Python爬虫实战一键采集大家喜欢的DJ舞曲哦&#xff01; …...

[SSD综述 1.5] SSD固态硬盘参数图文解析_选购固态硬盘就像买衣服?

版权声明&#xff1a;付费作品&#xff0c;未经许可&#xff0c;不可转载前言SSD &#xff08;Solid State Drive&#xff09;&#xff0c;即固态硬盘&#xff0c;通常是一种以半导体闪存&#xff08;NAND Flash&#xff09;作为介质的存储设备。SSD 以半导体作为介质存储数据&…...

SAP Insurance Analyzer

SAP Insurance Analyzer 是一款用于保险公司财务和风险管理的软件。SAP Insurance analyzer 支持基于 IFRS 17 或 Solvency II 的保险合同估值和计算要求。SAP Insurance Analyzer 于 2013 年 5 月推出&#xff0c;为源数据和结果数据集成了一个预配置的保险数据模型。 源数据…...

自动化测试 ——自动卸载软件

在平常的测试工作中&#xff0c;经常要安装软件&#xff0c;卸载软件, 即繁琐又累。 安装和卸载完全可以做成自动化。 安装软件我们可以通过自动化框架&#xff0c;自动点击Next,来自动安装。 卸载软件我们可以通过msiexec命令行工具自动化卸载软件 用msiexec 命令来卸载软件 …...

05 封装

在对 context 的封装中&#xff0c;我们只是将 request、response 结构直接放入 context 结构体中&#xff0c;对应的方法并没有很好的封装。 函数封装并不是一件很简单、很随意的事情。相反&#xff0c;如何封装出易用、可读性高的函数是非常需要精心考量的&#xff0c;框架中…...

clean

clean code 记得以前写过这题&#xff0c;写的乱七八糟&#xff0c;分析来分析去。 后悔应该早点写代码&#xff0c;leetcode大一就该刷了。 https://leetcode.cn/problems/plus-one/submissions/ class Solution { public:vector<int> plusOne(vector<int>&…...

佛科院计算机软件技术基础——线性表

一、基础知识了解&#xff1a;结构体的理解&#xff1a;我们知道整型是由1位符号位和15位数值位组成&#xff0c;而就可以把结构体理解为我们定义的数据类型&#xff0c;如&#xff1a;typedef struct {int data[2]; //存储顺序表中的元素int len; …...

linux下终端操作mysql数据库

目录 一&#xff0e;检查mysql是否安装 1. 查看文件安装路径 2. 查询运行文件所在路径(文件夹地址) 二&#xff0e;登录mysql 三&#xff0e;列出mysql全部用户 四&#xff0e;常用指令 &#xff11;&#xff0e;查看全部数据库 &#xff12;&#xff0e;选择数据库 …...

MySQL参数优化之thread_cache_size

1.thread_cache_size简介 每建立一个连接&#xff0c;都需要一个线程来与之匹配&#xff0c;此参数用来缓存空闲的线程&#xff0c;以至不被销毁&#xff0c;如果线程缓存中有空闲线程&#xff0c;这时候如果建立新连接&#xff0c;MYSQL就会很快的响应连接请求。 show statu…...

gRPC服务健康检查(二):gRPC健康检查协议详解

gRPC健康检查协议健康检查用于检测服务端能否正常处理rpc请求&#xff0c;客户端对服务端的健康检查可以点对点进行&#xff0c;也可以通过某些控制系统&#xff08;如负载平衡&#xff09;进行。客户端可以根据服务端返回的状态执行对应的策略。因为GRPC服务可以用于简单的客户…...

Android系统10 RK3399 init进程启动(四十七) Android init 进程整体代码逻辑简述

配套系列教学视频链接&#xff1a;安卓系列教程之ROM系统开发-百问100ask说明系统&#xff1a;Android10.0设备&#xff1a; FireFly RK3399 &#xff08;ROC-RK3399-PC-PLUS&#xff09;前言本文简单描述一下android init祖先进程启动的基本执行流程&#xff0c;让大家有一个整…...

CSDN 编程竞赛三十二期题解

竞赛总览 CSDN 编程竞赛三十二期&#xff1a;比赛详情 (csdn.net) 竞赛题解 题目1、传奇霸业 传奇霸业&#xff0c;是兄弟就来干。小春&#xff08;HP为a&#xff09;遇到了一只黄金哥布林&#xff08;HP为x&#xff09;。小春每次能对哥布林造成b点伤害&#xff0c;哥布林…...

Kubernetes 中的 Pod Hook

Pod Hook 我们知道Pod是Kubernetes集群中的最小单元&#xff0c;而 Pod 是有容器组组成的&#xff0c;所以在讨论 Pod 的生命周期的时候我们可以先来讨论下容器的生命周期。 实际上 Kubernetes 为我们的容器提供了生命周期钩子的&#xff0c;就是我们说的Pod Hook&#xff0c…...

Linux操作系统安装MySQL(rpm安装)

Linux操作系统安装MySQL&#xff08;rpm安装&#xff09;1 背景2 环境说明3 准备工作3.1 端口查看3.2 检查安装3.3 创建MySQL用户和组4 MySQL安装4.1 下载MySQL4.2 解压安装包4.3 安装MySQL4.4 初始化MySQL4.5 启动MySQL4.6 设置MySQL初始密码4.6.1 查看数据库初始密码4.6.2 更…...

MySQL高级第二讲

目录 二、MySQL高级02 2.1 触发器 2.1.1 触发器介绍 2.1.2 创建触发器 2.2 MySQL的体系结构 2.3 存储引擎 2.3.1 存储引擎概述 2.3.2 各种存储引擎特性 2.3.3 InnoDB 2.3.4 MyISAM 2.3.5 MEMORY 2.3.6 MERGE 2.3.7 存储引擎的选择 2.4 优化sql 2.4.1 查看sql执行…...

凸优化专题1

多变量函数的求导与求梯度/矩阵求导 1. 导数 定义: 设f:Rn→Rm,且x∈intdomf,则f在点x的导数(或称Jacobian)记为矩阵Df(x)∈Rmnf:\R^n \rightarrow \R^m, 且x\in \mathbf{int}\ \mathbf{dom} f, 则f 在点x的导\\数(或称Jacobian)记为矩阵 Df(x) \in \R^{m\times n}f:Rn→Rm,且…...

【蓝桥杯每日一题】递推算法

&#x1f34e; 博客主页&#xff1a;&#x1f319;披星戴月的贾维斯 &#x1f34e; 欢迎关注&#xff1a;&#x1f44d;点赞&#x1f343;收藏&#x1f525;留言 &#x1f347;系列专栏&#xff1a;&#x1f319; 蓝桥杯 &#x1f319;我与杀戮之中绽放&#xff0c;亦如黎明的花…...

告别Charles/Fiddler抓包失败:用Magisk TrustUserCerts模块搞定安卓HTTPS拦截

安卓HTTPS抓包全攻略&#xff1a;从Magisk证书安装到防御绕过实战 移动应用安全测试中&#xff0c;HTTPS流量拦截是基础却关键的环节。随着Android系统安全机制的不断升级&#xff0c;传统的抓包方法在Android 7.0及更高版本上频频失效。本文将系统性地介绍基于Magisk的解决方案…...

深入解析服务器License管理:从基础命令到实战应用

1. 服务器License管理&#xff1a;为什么它比你想的更重要 如果你管理过服务器&#xff0c;尤其是那些运行着像CAD、EDA、仿真分析这类专业软件的服务器&#xff0c;那你肯定对“License”这个词不陌生。它就像软件的“通行证”&#xff0c;没有它&#xff0c;再强大的硬件也只…...

3步掌握文字转手写工具:免费高效实用指南

3步掌握文字转手写工具&#xff1a;免费高效实用指南 【免费下载链接】text-to-handwriting So your teacher asked you to upload written assignments? Hate writing assigments? This tool will help you convert your text to handwriting xD 项目地址: https://gitcod…...

SDMatte效果对比图谱:SDMatte/RemBG/BackgroundMattingV2在玻璃场景PK

SDMatte效果对比图谱&#xff1a;SDMatte/RemBG/BackgroundMattingV2在玻璃场景PK 1. 引言&#xff1a;玻璃抠图的特殊挑战 玻璃材质因其透明和反光特性&#xff0c;一直是图像抠图领域最具挑战性的对象之一。传统抠图工具在处理玻璃制品时&#xff0c;往往会出现边缘断裂、透…...

第10章 RTOS 感知调试(OpenOCD)

第10章 RTOS 感知调试 导读:在嵌入式开发中,RTOS(实时操作系统)的使用非常普遍。然而当多个线程并发执行时,传统的单线程调试方式无法感知任务切换和线程上下文,给问题定位带来极大困难。OpenOCD 内置了对十余种主流 RTOS 的线程感知调试支持,能够在暂停目标时自动识别所…...

别再只盯着标定板了!用ROS camera_calibration搞定海康工业相机,这5个细节决定成败

工业相机标定进阶指南&#xff1a;ROS camera_calibration的五个关键优化点 工业相机的标定质量直接决定了机器视觉系统的测量精度。许多开发者虽然能够完成基础标定流程&#xff0c;却常常在参数解读和精度优化环节遇到瓶颈。本文将深入解析ROS camera_calibration工具在实际工…...

避坑指南:VSCode Remote-SSH离线安装时,插件版本不兼容和服务器环境配置的那些坑

深度解析VSCode Remote-SSH离线安装的五大核心难题与实战解决方案 在远程开发日益普及的今天&#xff0c;VSCode的Remote-SSH功能已经成为开发者连接Linux服务器的首选工具。然而当网络环境受限时&#xff0c;离线安装过程中的各种"暗坑"往往让开发者寸步难行。本文将…...

避坑指南:在RV1103B上为SC132GS摄像头添加设备树节点的正确姿势

RV1103B平台SC132GS摄像头设备树配置实战指南 1. 瑞芯微RV1103B平台摄像头开发概述 在嵌入式视觉系统开发中&#xff0c;瑞芯微RV1103B凭借其出色的图像处理能力和低功耗特性&#xff0c;成为工业视觉、智能门铃等场景的热门选择。SC132GS作为一款高性价比的1/3英寸CMOS传感器&…...

如何让经典游戏完美运行在现代Windows系统:DDrawCompat高效解决方案全指南

如何让经典游戏完美运行在现代Windows系统&#xff1a;DDrawCompat高效解决方案全指南 【免费下载链接】DDrawCompat DirectDraw and Direct3D 1-7 compatibility, performance and visual enhancements for Windows Vista, 7, 8, 10 and 11 项目地址: https://gitcode.com/g…...

从时频分析到信号净化:小波变换的降噪实战指南

1. 小波变换基础&#xff1a;从傅里叶到时频分析 第一次接触小波变换时&#xff0c;我和大多数工程师一样&#xff0c;脑子里全是傅里叶变换的影子。记得当时处理一组振动传感器数据&#xff0c;傅里叶变换告诉我信号里存在30Hz和50Hz的成分&#xff0c;但就是找不到这些频率具…...