Java【多线程】synchronized关键字

目录

synchronized的特性

1.互斥

2.可重入

如何自己实现一个可重入锁?

关于死锁

死锁的第三种情况 N个线程M把锁

构成死锁的四个必要条件

java标准库中的线程安全类

线程不安全

线程安全


 

synchronized关键字-监视器锁monitor locker

synchronized的特性

1.互斥

synchronized 会起到互斥效果, 某个线程执⾏到某个对象的 synchronized 中时, 其他线程如果也执⾏到同⼀个对象 synchronized 就会阻塞等待
进⼊ synchronized 修饰的代码块, 相当于 加锁
退出 synchronized 修饰的代码块, 相当于 解锁

多个线程针对同一个锁对象加锁才会产生互斥(锁冲突/锁竞争)

synchronized(锁对象){//加锁

一些要保证安全的代码

}//解锁

synchronized修饰普通方法,相当于个给this加锁

synchronized修饰静态方法,相当于给类对象加锁

2.可重入

synchronized 同步块对同⼀条线程来说是可重⼊的,不会出现⾃⼰把⾃⼰锁死的问题
如何理解把自己锁死
public void add(){synchronized(this){count++;}
}
synchronized(counter){counter.add();
}

一旦调用的层次较深就容易出现这样的情况

要想解除阻塞需要往下执行才可以

要往下执行就要等到第一次的锁被释放

这样的问题就称为“死锁”dead lock

1.第一次加锁能成功(锁没有人使用)

2.第二次进行加锁,此时意味着锁对象已经被占用的状态,第二次加锁就会触发阻塞等待

死锁是一个非常严重的bug使代码执行到这一块之后就卡住

为了解决上述的问题,java的synchronized就引入了可重入的概念

当某个线程针对一个锁加锁成功之后

后续该线程再次针对这个锁进行加锁

不会触发阻塞而是直接往下走

因为当前这把锁就是被这个线程持有

但是,如果是其他线程尝试加锁就会正常阻塞

可重入锁的实现原理,关键在于让锁对象内部保存,当前是哪个线程持有的这把锁

后续有线程针对这个锁加锁的时候,对比一下,锁的持有者的线程是否和当前加锁的线程是同一个

synchronized(counter){//真正加锁 1synchronized(counter){//放行 2synchronized(counter){//放行 3counter.add();}//3->2}//2->1
}//1->0

最外层,真正加锁

最外层,真正解锁

站在jvm的视角,看到多个}需要执行

jvm如何知道哪个}是真正解锁的那个

先引入一个变量,计数器(0)

每次触发{的时候,count++;

每次触发}的时候,count--;

当count--为0的时候就是真正需要解锁的时候


如何自己实现一个可重入锁?

1.在锁内部记录当前是哪个线程持有的锁,后续每次加锁,都进行判定

2.通过计数器,记录当前加锁的次数,从而确定何时真正进行解锁


关于死锁

一个线程,一把锁,连续加锁两次

两个线程,两把锁,每个线程获取到一把锁之后,尝试获取对方的锁

           synchronized (locker1){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker2){System.out.println("t1线程两个锁都获取到");}}

必须是,拿到第一把锁,再拿第二把锁(不能释放第一把锁)

Thread-0因为竞争锁的缘故而阻塞了


如果不加sleep是否还会出现一样的现象

不一定

这就看具体的顺序

加上sleep为了确保

t1拿到locker1

t2拿到locker2

等待1s

t1拿到locker2

t2拿到locker1

如果不加sleep,很可能t1一口气就把locker1和locker2都拿到了,这个时候t2还没开动

自然无法构成死锁


让你手写一个出现死锁的代码

c++方向直接加锁两次就行了

java方向就得通过上述代码两个线程两把锁,精确控制好加锁的顺序


死锁的第三种情况 N个线程M把锁

一个经典模型,哲学家就餐问题

ef9a296fdc724afbbb313e8749163c92.png

5个哲学家随机触发吃面条和思考人生

5个哲学家就相当于5个线程

5根筷子就相当于5把锁

每个线程只需拿到其中的两根筷子即可

大部分情况下上述模型可以很好运转

一些极端情况下会造成死锁

同一时刻大家都想吃面条

同时拿起左手的筷子

此时任何一个线程都无法拿起右手的筷子


如何避免代码中出现死锁呢

死锁是怎样构成的

构成死锁的四个必要条件

1.锁是互斥的.(锁的基本性质)一个线程拿到锁之后,另一个线程再尝试获取锁,必须要阻塞等待

2.锁是不可抢占(不可剥夺)的.(锁的基本特性)线程1拿到锁,线程2也尝试获取这个锁,线程2必须阻塞等待,而不是线程2直接把锁抢过来

//至少,java的synchronized是遵守这两点

3.请求和保持.一个线程拿到锁1后,不释放锁2的前提下,获取锁2

如果先放下左手的筷子,再拿右手的筷子就不构成死锁

代码加锁的时候不要去“嵌套”

这种做法通用性不够的

有些情况确实需要拿到多个锁再进行某个操作

4.循环等待.多个线程多把锁之间的等待过程,构成了“循环”

a等待b,b等待a或者a等待b,b等待c,c等待a

约定好加锁的顺序就可以破除循环等待了

约定,每个线程加锁的时候

永远是先获取序号小的锁

后获取序号大的锁

//破坏掉上述的3或4任何一个条件都能够打破死锁


java标准库中的线程安全类

数据结构,集合类

线程不安全

ArrayList

LinkedList

HashMap

TreeMap

HashSet

TreeSet

StringBuilder

集合类自身没有进行任何加锁限制

线程安全

Vector(不推荐使用)

HashTable(不推荐使用)

ConcurrentHashMap

StringBuffer

在关键方法加了synchronized

不是写了synchronized就100%线程安全

得具体代码具体分析

加锁这个事情不是没有代价的

一旦代码中使用了锁意味着代码可能会因为锁的竞争产生阻塞=>程序执行的效率大打折扣

一定要思考清楚这个地方是否确实需要锁

不需要的时候不要乱加

还有的虽然没有加锁,但是不涉及“修改”,仍然是线程安全的

String

 

 

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

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

相关文章

Tailscale自建中转服务器derper搭建笔记(基于docker)

自己搭建derper服务器,让Tailscale中转更流畅。 Tailscale是很好的远程组网工具,在两台机器P2P打洞成功的情况下可以实现网络直连,但如果打洞失败就会进行数据中转,我们的数据要跑到国外再跑回来,这样速度就很慢了。 …

STGCN解读(论文+代码)

一、引言 引言部分不是论文的重点,主要讲述了交通预测的重要性以及一些传统方法的不足之处。进而推出了自己的模型——STGCN。 二、交通预测与图卷积 第二部分讲述了交通预测中路图和图卷积的概念。 首先理解道路图,交通预测被定义为典型的时间序列预测…

Axure重要元件一——动态面板

亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢! 本节课:动态面板 课程内容:认识动态面板、动态面板基本操作 应用场景:特定窗口、重要交互、长页面、容器等 一、认识动态面板 动态…

DeBiFormer:带有可变形代理双层路由注意力的视觉Transformer

https://arxiv.org/pdf/2410.08582v1 摘要 带有各种注意力模块的视觉Transformer在视觉任务上已表现出卓越的性能。虽然使用稀疏自适应注意力(如在DAT中)在图像分类任务中取得了显著成果,但在对语义分割任务进行微调时,由可变形…

bug的定义和测试

一、软件测试的生命周期 软件测试的⽣命周期是指测试流程,这个流程是按照⼀定顺序执⾏的⼀系列特定的步骤,去保证产品 质量符合需求。在软件测试⽣命周期流程中,每个活动都按照计划的系统的执⾏。每个阶段有不同的 ⽬标和交付产物 需求分析…

【python+Redis】hash修改

文章目录 前请详解一、关于Update1. 语法2. 代码示例 二、完整代码 前请详解 Redis库数据 keyvalue1{“id”: 1, “name”: “xxx”, “age”: “18”, “sex”: “\u7537”}2{“id”: 2, “name”: “xxx”, “age”: “18”, “sex”: “\u5973”}3{“id”: 3, “name”: “…

软件测试面试题600多条及答案

这些问题都是软件测试领域常见的面试问题,以下是一些可能的答案: 什么是软件测试? 软件测试是一系列活动,旨在评估软件产品的质量和性能,以确保它符合规定的需求和标准。它包括执行程序或系统以验证其满足规定需求的过…

“探索Adobe Photoshop 2024:订阅方案、成本效益分析及在线替代品“

设计师们对Adobe Photoshop这款业界领先的图像编辑软件肯定不会陌生。如果你正考虑加入Photoshop的用户行列,可能会对其价格感到好奇。Photoshop的价值在于其强大的功能,而它的价格也反映了这一点。下面,我们就来详细了解一下Adobe Photoshop…

数据结构(8.2_1)——插入排序

插入排序 算法思想&#xff1a;每次将一个待排序的记录按其关键字大小插入到前面已排序好的子序列中&#xff0c;直到全部记录插入完成。 代码实现 #include <stdio.h>void InsertSort(int A[], int n) {int i, j.temp;for (i 1; i < n; i) {//将各元素插入已排好…

【每日一题】LeetCode每日一题-无重复字符的最长子串

题目链接&#xff1a;https://leetcode.cn/problems/longest-substring-without-repeating-characters/description/ 题目描述&#xff1a; 给定一个字符串 s&#xff0c;找到其中不包含重复字符的最长子串的长度。 示例 1: 输入: s "abcabcbb" 输出: 3 解释: 因…

Axure重要元件二——内联框架

亲爱的小伙伴&#xff0c;在您浏览之前&#xff0c;烦请关注一下&#xff0c;在此深表感谢&#xff01; 课程主题&#xff1a;内联框架 课程内容&#xff1a;认识内联框架、基本嵌入 应用场景&#xff1a;表单、图片、文字嵌入式场景、交互应用 一、认识内联框架 内联框架的…

如何安全擦除 iPhone 上的所有数据,避免隐私泄露?

在当今的数字时代&#xff0c;隐私安全尤为重要。特别是在转让或出售 iPhone 之前&#xff0c;擦除设备上的所有内容是每位用户都应注意的关键步骤。尽管苹果自带了删除数据的功能&#xff0c;但有时这并不足以保证数据完全无法恢复。本文将结合 iPhone 自带的"抹掉所有内…

软考(中级-软件设计师)计算机系统篇(1018)

十、存储系统 10.1 层次结构主存–辅存&#xff1a;实现虚拟存储系统&#xff0c;解决了主存容量不够的问题。 Cache–主存&#xff1a;解决了主存与CPU速度不匹配的问题。 10.2 分类 1、按位置分类&#xff1a;可分为内存和外存。 内存&#xff08;主存&#xff09;&#…

Erlang的吸睛特性:热升级功能的工作和实现原理

Erlang的热升级功能允许开发者在不停止系统的情况下进行代码更新。这一机制是Erlang语言的核心优势之一&#xff0c;特别适用于需要高可用性的分布式系统。下面通过源代码来剖析其工作原理。 1. 模块定义 Erlang中的每个模块通常包含多个功能。在热升级过程中&#xff0c;旧版…

【从零开发Mybatis】引入XNode和XPathParser

引言 在上文&#xff0c;我们发现直接使用 DOM库去解析XML 配置文件&#xff0c;非常复杂&#xff0c;也很不方便&#xff0c;需要编写大量的重复代码来处理 XML 文件的读取和解析&#xff0c;代码可读性以及可维护性相当差&#xff0c;使用起来非常不灵活。 因此&#xff0c…

o1快慢思考的风又吹到了Agent!

智能体&#xff08;Agent&#xff09;通过自然对话与用户互动有两个任务&#xff1a;交谈和规划/推理。对话回应必须基于所有可用信息&#xff0c;行动必须有助于实现目标。与用户交谈和进行多步推理和规划之间的二分法&#xff0c;类似卡尼曼引入的人类快速思考和慢速思考系统…

库卡ForceTorqueControl(二)

1. 基准坐标系RCS 基准坐标系 RCS 是力 / 力矩控制的参考系。基准坐标系的原点始终是当前的TCP。 1.1 BASE 的 RCS 姿态 基准坐标系的姿态与当前基础坐标系&#xff08;基座坐标系&#xff09;的姿态一致。它不取决于刀具的姿态。基准坐标系的原点是当前的 TCP。 示例&#xff…

【数据库设计】概念结构设计

引入——整体解释 上次我们讲完了关系模型&#xff0c;这次我们来讲新的章节&#xff1a;数据库设计 该怎样有效地管理和存储现实中的数据&#xff1f;答案是设计一个优秀的数据库。现实中的数据转化成关系表中的数据需要经过四个主要的设计步骤。 现实世界需求分析——>…

JavaScript ES6 新特性全览:变量声明、函数语法、数据结构等多方面解析

在现代 JavaScript 开发中&#xff0c;ECMAScript 6&#xff08;简称 ES6&#xff09;带来了许多强大的新特性&#xff0c;极大地提升了开发效率和代码的可读性。本文将带你全面了解 ES6 的主要新特性。 一、let 和 const 关键字 let和const是 ES6 中引入的新的变量声明方式&…

JAVA地狱级笑话

为什么Java开发者总是不怕黑暗&#xff1f; 因为他们总是有null指针来照亮路。 Java程序员最讨厌的音乐是什么&#xff1f; Garbage Collection旋律&#xff0c;节奏总是让他们烦躁。 为什么Java中的HashMap很擅长社交&#xff1f; 因为它总是能快速找到key对应的朋友。 Java开…