<JavaEE> synchronized关键字和锁机制 -- 锁的特点、锁的使用、锁竞争和死锁、死锁的解决方法

目录

一、synchronized 关键字简介

二、synchronized 的特点 -- 互斥

三、synchronized 的特点 -- 可重入

四、synchronized 的使用示例

4.1 修饰代码块 - 锁任意实例

4.2 修饰代码块 - 锁当前实例

4.3 修饰普通方法 - 锁方法所在实例

4.4 修饰代码块 - 锁指定类对象

4.5 修饰静态方法 - 锁方法所在类对象

五、锁竞争和死锁

5.1 出现死锁的三种典型场景

5.1.1 “重复锁”

5.1.2 “互相锁”

5.1.3 “复杂锁”

5.2 死锁产生的必要条件

5.3 解决死锁的方案


一、synchronized 关键字简介

概述:Java中加锁的方式有很多种,其中使用 synchronized 关键字进行加锁是最常用的。synchronized 是一种监视器锁(monitor lock)。
加锁的目的:是为了将多个操作“打包”为一个有“原子性”的操作。
加锁的核心规则:进行加锁的时候必须先准备好“锁对象”,锁对象可以是任何类型的实例。
synchronized 的底层实现:synchronized 的底层是使用操作系统的 mutex lock 实现的,本质上依然是调用系统的 API ,依靠 CPU 的特定指令完成加锁功能的。

二、synchronized 的特点 -- 互斥

1)什么是互斥?
某个对象使用了 synchronized 进行修饰,当一个线程访问这个对象时,就会加锁,其他线程想要访问这个对象,就会先阻塞等待,直到这个对象解锁。这就是使用 synchronized 关键字时产生的互斥效果。
2)什么是加锁、解锁?

当程序进入由 synchronized 修饰的代码块、对象或方法时,即相当于加锁。

当程序退出由 synchronized 修饰的代码块、对象或方法时,即相当于加锁。

3)由互斥到冲突,什么是锁冲突/锁竞争?

由于 synchronized 具有互斥的特点,因此当多个线程同时竞争同一个锁时,线程间的冲突就不可避免。当有一个线程获得了锁,那么此时其他还想获得该锁的线程就只能阻塞等待,直到锁被释放后,才能再次竞争这个锁。这就是锁冲突或者说锁竞争。


三、synchronized 的特点 -- 可重入

1)什么是不可重入锁?

同一个线程在还没释放锁的情况下,访问同一个锁。

从 synchronized 的互斥特点可以了解到,当锁未被释放,访问该锁的线程会阻塞等待。

由于锁还没有释放,第二次加锁时,线程进入阻塞等待。

线程进入阻塞等待,则第一次的锁无法释放。

这样程序就进入了僵持状态。

这种状态被称为“死锁”。

而这样的锁,被称为“不可重入锁”。

2)什么是可重入锁?

可重入锁与不可重入锁不同,不会出现自己把自己锁死的情况。synchronized 就是可重入锁。

3)可重入锁是怎么实现可重入的?

可重入锁,锁内部会有两个属性,分别是“线程持有者”和“计数器”。

线程持有者

记录了当前锁是被哪一个线程持有的。

当发生重复加锁时,会判断是否是同一线程加锁。

如果是则跳过加锁步骤,只是在另一个属性“计数器”上自增1。

如果不是,则阻塞等待。

计数器

用于记录当前锁的加锁次数。

每次加锁,“计数器”计数会自增1(比如重复加锁10次,那么计数器的值就会等于10)。

每次解锁,“计数器”计数会自减1,当计数器的值归零时,才是真正的释放锁,此时该锁才能被其他线程获取。


四、synchronized 的使用示例

4.1 修饰代码块 - 锁任意实例

public class Test{
    //创建任意类型实例作为锁对象;
    Object locker = new Object();

    public void lockTest(){
        //使用synchronized,指定locker作为锁对象,在需要加锁的代码块上加锁;
        synchronized (locker) {
            //需要加锁的代码;
        }
    }
}

4.2 修饰代码块 - 锁当前实例

public class Test{
    public void lockTest(){
        //使用synchronized,指定this(当前实例)作为锁对象,在需要加锁的代码块上加锁;
        synchronized (this) {
            //需要加锁的代码;
        }
    }
}

4.3 修饰普通方法 - 锁方法所在实例

public class Test{
    //在普通方法上,使用synchronized,指定当前实例作为锁对象,将方法加锁;
    public synchronized void lockTest(){
        //需要加锁的代码;
    }
}

4.4 修饰代码块 - 锁指定类对象

//任意类;
public class Locker{

}

public class Test{
    public void lockTest(){
        //使用synchronized,指定class(类对象)作为锁对象,在需要加锁的代码块上加锁;
        synchronized (Locker.class) {
            //需要加锁的代码;
        }
    }
}

4.5 修饰静态方法 - 锁方法所在类对象

public class Test{
    //在静态方法上,使用synchronized,指定当前类对象作为锁对象,将方法加锁;
    public synchronized static void lockTest(){
        //需要加锁的代码;
    }
}

五、锁竞争和死锁

1)由锁竞争到死锁,什么是死锁?

上文在“synchronized 的特点 -- 互斥”中,介绍了什么是锁竞争。

人可以卷,但卷到一定程度就可以卷死自己或卷死别人。

那么,锁,也是可以卷的,比如锁竞争

加锁可以解决线程安全问题,但是如果加锁方式不当,就可能产生死锁。

2)死锁对程序来说意味着什么?

死锁是程序中最严重的一类BUG。

程序可能因此停摆、崩溃。

当然,人也可能因此“停摆、崩溃”。

5.1 出现死锁的三种典型场景

死锁有以下三种典型场景。
<1>

“重复锁”:如,一个线程,一把锁,自己把自己拷上了。

<2>“互相锁”:如,两个线程,两把锁,互相把对方拷上了。
<3>“复杂锁”:如,上述两种锁重复或复合发生的情况,多个线程多把锁,超级加倍。
以上三个锁的名字,是笔者归纳总结后,为方便记忆而概括出的锁名,不是公认的专业名词。

5.1.1 “重复锁”

“重复锁”是指什么情况?

锁在被释放前,同一个线程再次要求获得同一个锁。

锁没被释放,线程无法获得锁,进入阻塞。

但线程阻塞,代码就不会继续执行,锁也就一直得不到释放。

由此实现了自己卡死自己的“壮举”。

代码演示死锁:

    public static void main(String[] args) {
        //创建任意类型实例作为锁对象;
        Object locker = new Object();

        Thread t = new Thread(()->{
            //指定locker作为锁对象;
            synchronized (locker) {
                //再次指定locker作为锁对象;
                synchronized (locker){
                    //需要加锁的代码;
                }
            }
        });
    }
synchronized  是“可重入锁”。

“可重入锁”和“不可重入锁”的定义和区别,在上文“synchronized 的特点 -- 可重入”中说明了。

Java 提供的 synchronized 关键字,实现的是一个“可重入锁”。所以不用担心会发生这种死锁。

5.1.2 “互相锁”

1)“互相锁”是指什么情况?

两个线程,都获取了一个不同的锁。

但是在各自的锁释放前,又分别去获取了对方的锁。

但此时两把锁都还没有被释放,那么两个线程都进入阻塞等待的状态,都在等对方把锁释放。

代码演示死锁:

    public static void main2(String[] args) {
        //创建两个任意类型实例作为锁对象;
        Object locker1 = new Object();
        Object locker2 = new Object();

        Thread t1 = new Thread(()->{
            //指定locker1作为锁对象;
            synchronized (locker1) {
                //指定locker2作为锁对象;
                synchronized (locker2) {
                    //需要加锁的代码;
                }
            }
        });
        Thread t2 = new Thread(()->{
            //指定locker2作为锁对象;
            synchronized (locker2) {
                //指定locker1作为锁对象;
                synchronized (locker1) {
                    //需要加锁的代码;
                }
            }
        });
    }

5.1.3 “复杂锁”

“复杂锁”是指什么情况?

“复杂锁”指前两种情况重复发生,或复合发生时,锁与锁之间相互叠加、“犬牙交错”的局面。

图示演示死锁:

5.2 死锁产生的必要条件

产生死锁有以下四个必要条件:
<1>互斥,获取锁的过程需要是互斥的,当锁被一个线程获取,另一个线程想获取这把锁就必须阻塞等待。这是锁的基本特性之一。
<2>不可劫取。锁被一个线程获取后,另一个线程不能强行把锁抢走,除非锁被持有线程释放。这也是锁的基本特性之一。
<3>请求保持。当一个线程申请锁而进入阻塞等待时,对自己已经持有的锁保持持有状态。这个条件与代码结构相关。
<4>循环等待/环路等待。线程申请锁,而锁在等待线程释放,形成环路。这个条件与代码结构相关。

5.3 解决死锁的方案

解决死锁的方案有以下几种方法:
<1>超时放弃。线程进入阻塞等待,当等待时间超过预设时间,则获取锁失败,将持有的锁释放。
<2>依序加锁。指定加锁的顺序规则,所有线程都需要按照规则规定的加锁顺序进行加锁。

图示演示依序加锁:


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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【开源】基于JAVA语言的校园疫情防控管理系统

项目编号&#xff1a; S 037 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S037&#xff0c;文末获取源码。} 项目编号&#xff1a;S037&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 学生2.2 老师2.3 学校管理部门 三、…

【数据结构】二叉树---C语言版

二叉树 一、树的概念及结构1.树的概念2.树的相关概念3.树的表示4.树在实际中的应用 二、二叉树的概念及结构1.二叉树的概念2.满二叉树3.完全二叉树4.二叉树的性质5.二叉树的储存结构 三、二叉树的遍历1.前序遍历2.中序遍历3.后序遍历4.层序遍历 四、手撕二叉树&#xff08;务必…

基于JSP的网络考试系统/在线考试系统的设计与实现

摘 要 网络考试系统是由高校的一个网络考试&#xff0c;按照章程自主开展网络考试系统。网络考试是实施素质教育的重要途径和有效方式&#xff0c;在加强校园文化建设、提高学生综合素质、引导学生适应社会、促进学生成才就业等方面发挥着重要作用&#xff0c;是新形势下有效凝…

YOLOv7+姿态估计Pose+tensort部署加速

YOLOv7-Pose 实现YOLOv7&#xff1a;可训练的免费套件为实时目标检测设置了最新技术标准 YOLOv7-Pose的姿态估计是基于YOLO-Pose的。关键点标签采用MS COCO 2017数据集。 训练 使用预训练模型yolov7-w6-person.pt进行训练。训练命令如下&#xff1a; python -m torch.distr…

leetcode 3. 无重复字符的最长子串

代码&#xff1a; //采用滑动窗口来进行解决 class Solution {public int lengthOfLongestSubstring(String s) {//字符串由英文字母、数字、符号和空格组成&#xff0c;通过对应的 ASCLL 码作为下标在数组中记录出现的次数//判断字符在字串中是否重复出现int[] ascllnew int[…

前端对浏览器的理解

浏览器的主要构成 用户界面 &#xff0d; 包括地址栏、后退/前进按钮、书签目录等&#xff0c;也就是你所看到的除了用来显示你所请求页面的主窗口之外的其他部分。 浏览器引擎 &#xff0d; 用来查询及操作渲染引擎的接口。 渲染引擎 &#xff0d; 用来显示请求的内容&#…

深入理解同源限制:网络安全的守护者(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

我爱上这38个酷炫的数据大屏(附 Python 源码)

随着大数据的发展&#xff0c;可视化大屏在各行各业得到越来越广泛的应用。 可视化大屏不再只是电影里奇幻的画面&#xff0c;而是被实实在在地应用在政府、商业、金融、制造等各个行业的业务场景中&#xff0c;切切实实地实现着大数据的价值。 所以本着学习的态度&#xff0…

四、设置主机名和域名映射

目录 1、配置每台虚拟机主机名 2、配置每台虚拟机域名映射 1、配置每台虚拟机主机名

MATLAB实战 | S函数的设计与应用

S函数用于开发新的Simulink通用功能模块&#xff0c;是一种对模块库进行扩展的工具。S函数可以采用MATLAB语言、C、C、FORTRAN、Ada等语言编写。在S函数中使用文本方式输入公式、方程&#xff0c;非常适合复杂动态系统的数学描述&#xff0c;并且在仿真过程中可以对仿真进行更精…

ASP.NET版本WOL服务的使用

本文以WOL为例&#xff0c;演示如何通过 GPT-4 让其为 WebAPI 项目设计一个网页。其中介绍了如何让GPT4生成相关功能&#xff0c;添加动画效果&#xff0c;接口鉴权等。 1. 背景 前面我们已经完成了一个WOL服务的开发&#xff0c;并将其迁移改造为了 ASP.NET 服务并完成了部署…

02数仓平台Zookeeper

概述 ZooKeeper是一种分布式协调服务&#xff0c;用于管理大型主机集。在分布式环境中协调和管理服务是一个复杂的过程。ZooKeeper通过其简单的架构和API解决了这个问题。ZooKeeper允许开发人员专注于核心应用程序逻辑&#xff0c;而不必担心应用程序的分布式性质。 Zookeepe…

DBeaver 社区版(免费版)下载、安装、解决驱动更新出错问题

DBeaver 社区版&#xff08;免费版&#xff09; DBeaver有简洁版&#xff0c;企业版&#xff0c;旗舰版&#xff0c;社区版&#xff08;免费版&#xff09;。除了社区版&#xff0c;其他几个版本都是需要付费的&#xff0c;当然相对来说&#xff0c;功能也要更完善些&#xff…

uniApp打包的手机app如果用户没开启通知权限、引导用户开启

封装一个setPermissions.js文件 /*** 如果用户没开启通知权限、引导用户开启 */ export function setPermissions() {// #ifdef APP-PLUS if (plus.os.name Android) {var main plus.android.runtimeMainActivity();var pkName main.getPackageName();var uid main.getApp…

某公司前端笔试题(12.30)

1、对象数组去重&#xff1a; 数组去重&#xff1a; const a[{a:1,b:2},{a:2},{a:2},{a:1,c:3},{b:2,a:1}] 结果&#xff1a;[{a:1,b:2},{a:2},{a:1,c:3}] // 判断两个对象的属性值是否一致 const a [{ a: 1, b: 2 }, { a: 2 }, { a: 2 }, { a: 1, c: 3 }, { b: 2, a: 1 }] co…

elupload base64

创作灵感也许就是这会儿还没有入睡吧&#xff0c;对接百度图片OCR功能&#xff0c;需要将图片转为BASE64上传调用百度的接口api&#xff0c;进行研究实现。页面如下&#xff0c;点击后选择图片文件后不是直接上传&#xff0c;而是获取图片的bytes数据&#xff01; <el-uploa…

2012-2021年银行数字化转型程度数据(根据年报词频计算)

2012-2021年银行数字化转型程度&#xff08;根据年报词频计算&#xff09; 1、时间&#xff1a;2012-2021年 2、指标&#xff1a;银行名称、年份、数字化转型程度 3、范围&#xff1a;52家银行&#xff08;上海银行、中信银行、中国银行、交通银行、光大银行、兰州银行、兴业…

国标GBT 27930关键点梳理

1、充电总流程 整个充电过程包括六个阶段:物理连接完成、低压辅助上电、充电握手阶段、充电参数配置阶段、充电阶段和充电结束阶段。 在各个阶段,充电机和 BMS 如果在规定的时间内没有收到对方报文或没有收到正确报文,即判定为超时(超时指在规定时间内没有收到对方的完整数据包…

每日一练2023.12.2——正整数A+B【PTA】

题目链接&#xff1a;L1-025 正整数AB 题目要求&#xff1a; 题的目标很简单&#xff0c;就是求两个正整数A和B的和&#xff0c;其中A和B都在区间[1,1000]。稍微有点麻烦的是&#xff0c;输入并不保证是两个正整数。 输入格式&#xff1a; 输入在一行给出A和B&#xff0c;…

webGIS使用JS,高德API完成简单的智慧校园项目基础

代码实现 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-width, i…