Java多线程(4)---死锁和Synchronized加锁流程

news2024/10/7 9:21:21

目录

前言

一.synchronized

1.1概念 

1.2Synchronized是什么锁?

1.3Synchronized加锁工作过程

1.4其他优化操作

二.死锁

2.1什么是死锁

2.2死锁的几个经典场景

2.3死锁产生的条件

2.4如何解决死锁


🎁个人主页:tq02的博客_CSDN博客-C语言,Java,Java数据结构领域博主
🎥 本文由 tq02 原创,首发于 CSDN🙉
🎄 本章讲解内容:线程的策略锁、CAS和JUC

🎥多线程学习专栏:多线程学习专栏

🎥其他学习专栏:  C语言         JavaSE       MySQL基础 

前言

        在多线程的讲解当中,我们可以知道synchronized是加锁操作,让两个线程发生互斥效果,在代码中使用synchronized关键字来实现锁的获取和释放。如果是刚刚接触多线程的人,我希望你可以从第一章多线程开始学习:http://t.csdn.cn/0vEhY

一.synchronized

1.1概念 

        Synchronized是Java中内置的锁机制,用于实现线程同步。它可以通过在代码中使用synchronized关键字来实现锁的获取和释放。Synchronized关键字可以用在方法上或者代码块中。当一个线程执行到synchronized修饰的代码块时,它会尝试获取锁,如果锁没有被其他线程占用,则获取成功,执行代码块中的内容。如果锁已经被其他线程占用,则该线程会进入等待状态,直到获取到锁才能继续执行。

1.2Synchronized是什么锁?

  1. 开始时是乐观锁, 如果锁冲突频繁, 就转换为悲观锁.
  2. 开始是轻量级锁实现, 如果锁被持有的时间较长, 就转换成重量级锁.
  3. 实现轻量级锁的时候大概率用到的自旋锁策略
  4. 是一种不公平锁
  5. 是一种可重入锁
  6. 不是读写锁
     

注:需要使用公平锁,建议使用ReentrantLock来实现。ReentrantLock提供了公平锁和非公平锁两种模式,通过构造函数的参数来指定锁的模式。

1.3Synchronized加锁工作过程

        对于锁资源只有一个或者两个线程交替竞争的,仍然需要使用系统调用,无疑对CPU资源是极大的消耗。因此,在jdk1.6针对Synchronized加锁进行了优化。按对锁的竞争程度划分成:无锁,偏向锁,轻量级锁,重量级锁。简单而言就是从无锁-->重量级锁。 

无锁

当你添加了锁时,如果编译器认为不需要加锁,会自动删除,因此便是无锁

偏向锁

偏向锁不是真的 "加锁", 只是给对象头中做一个 "偏向锁的标记", 记录这个锁属于哪个线程.
如果后续没有其他线程来竞争该锁, 那么就不用进行其他同步操作了(避免了加锁解锁的开销)
如果后续有其他线程来竞争该锁(刚才已经在锁对象中记录了当前锁属于哪个线程了, 很容易识别当前申请锁的线程是不是之前记录的线程), 那就取消原来的偏向锁状态, 进入一般的轻量级锁状态.
注:相当于做个标记,相当于 "延迟加锁" . 能不加锁就不加锁, 尽量避免不必要的加锁开销.

轻量级锁
随着其他线程进入竞争, 偏向锁状态被消除, 进入轻量级锁状态(自适应的自旋锁).
此处的轻量级锁就是通过 CAS 来实现

  1. 通过 CAS 检查并更新一块内存 (比如 null => 该线程引用)
  2. 如果更新成功, 则认为加锁成功
  3. 如果更新失败, 则认为锁被占用, 继续自旋式的等待(并不放弃 CPU).

注:此处的自旋锁不会一种持续进行,而是达到一定的时间/重试次数, 就不再自旋了.

重量级锁
        如果锁竞争进一步激烈, 自旋不能快速获取到锁状态, 就会膨胀为重量级锁

此处的重量级锁就是指用到内核提供的 mutex .
具体流程:

  • 执行加锁操作, 先进入内核态.
  • 在内核态判定当前锁是否已经被占用
  • 如果该锁没有占用, 则加锁成功, 并切换回用户态.
  • 如果该锁被占用, 则加锁失败. 此时线程进入锁的等待队列, 挂起. 等待被操作系统唤醒.
  • 经历了一系列的沧海桑田, 这个锁被其他线程释放了, 操作系统也想起了这个挂起的线程, 于是唤醒
  • 这个线程, 尝试重新获取锁

1.4其他优化操作

        我们额外补充2个编译器对锁的优化操作。锁消除和锁粗化

锁消除

        代码中, 用到了 synchronized, 但其实没有在多线程环境下. (例如 StringBuffer)

StringBuffer tq02 = new StringBuffer();
tq02.append("a");
tq02.append("b");
tq02.append("c");
tq02.append("d");

每个 append 的调用都会涉及加锁和解锁. 但如果只是在单线程中执行这个代码, 那么这些加
锁解锁操作是没有必要的, 白白浪费了一些资源开销.因此将锁给优化了。

锁粗化

锁的粗化是根据锁的粒度:粗和细

 实际开发过程中, 使用细粒度锁, 是期望释放锁的时候其他线程能使用锁.但可能并没有其他线程来抢占这个锁. 这种情况 JVM 就会自动把锁粗化, 避免频繁申请释放锁.
 

二.死锁

2.1什么是死锁

        死锁是指在多进程系统中,每个进程都在等待某个资源,而该资源又被其他进程占用,导致所有进程都无法继续执行的状态。

例如:A、B、C、D和E去上厕所,A进入厕所并且锁门,B.C.D等待,可是A刚刚进入厕所,因为特殊的原因,凭空转移到了外面,A就得重新排队,可是门还是锁着的啊,因此导致了死锁。

2.2死锁的几个经典场景

经典场景有:

  1. 一个线程,一把锁
  2. 两个线程,两把锁
  3. 多个线程,多把锁

1.一个线程,一把锁

        一个线程连续被同一个加锁两次,如果是不可重入锁,那么会是死锁。

解析:我去上厕所,我把厕所门锁住,再通过厕所的窗户出去,然后再来上厕所,发现厕所锁住了,就耐心等待,却没想过这是自己锁的。

代码实现:

public class Counter {
        
   void increase() {       
     synchronize(this){
       increase()   //可以理解为翻窗逃走,第二次加锁时,是锁了的
    }
}

public static void main(String[] args) throws InterruptedException {
    final Counter counter = new Counter();
        Thread t1 = new Thread(() -> {
                counter.increase();
        });
            t1.start();
    }
}

2.两个线程,两把锁

        线程1先获取锁A,再尝试获取锁B,同时,线程2先获取锁B,再尝试获取锁A,此时两个线程就会互相僵住,谁都获取不到对方持有的锁。

解析:我在汽车里,车钥匙在我妻子手上,我出不来,我妻子在房间里,房间钥匙在我手上,我妻子也出不来,导致双方被锁,导致死锁。

代码示例:

public class Test {
    public static void main(String[] args) {
        //2个锁对象
        Object lockerA = new Object();
        Object lockerB = new Object();
 
        Thread t1 = new Thread(() -> {
            System.out.println("t1尝试获取锁A");
            synchronized (lockerA){
                System.out.println("t1获取到锁A");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t1尝试获取锁B");
                synchronized (lockerB){
                    System.out.println("t1获取到锁B");
                }
            }
        });
        Thread t2 = new Thread(() -> {
            System.out.println("t2尝试获取锁B");
            synchronized (lockerB){
                System.out.println("t2获取到锁B");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t2尝试获取锁A");
                synchronized (lockerA){
                    System.out.println("t2获取到锁A");
                }
            }
        });
 
        t1.start();
        t2.start();
    }
}

3.多个线程,多把锁

        很明显啊,两把锁,两个线程也有问题,更何况是多把锁啊,在这方面最经典的是"哲学家就餐问题"。

如图:火柴人是哲学家、红线是筷子,每一个哲学家的左右都有一根筷子。规定,当有一根哲学家饿了,会先拿起左边的筷子,然后再拿右边的筷子,吃完了就放下筷子。

造成死锁问题:每一个哲学家都饿了,然后都拿起了左边的筷子,可是当拿右边的筷子时,发现有其他人在使用,所以导致阻塞,然后一直等待别人吃饱放下筷子,可是每个人都在等待。

2.3死锁产生的条件

死锁产生需要四个条件:

  1. 互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用
  2. 不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
  3. 请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
  4. 循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。

当上述四个条件都成立的时候,便形成死锁。当然,死锁的情况下如果打破上述任何一个条件,便可让死锁消失。
 

2.4如何解决死锁

        想没有死锁,那么我们可以从死锁产生的条件入手,只有破坏其他一条就可以了。 互斥使用和不可抢占是锁的基本特性,因此无法干预,但是请求和保持,也不可能改变,因为这是代码执行逻辑。因此只有循环等待,我们可以打破

        为了解决死锁问题,可以采取预防、避免、检测和解除四种方法。

预防:通过设置某些限制条件,以防止死锁的发生。

避免:系统在分配资源时根据资源的使用情况提前作出预测,从而避免死锁的发生。

检测:允许系统在运行过程中产生死锁,但系统中有相应的管理模块可以及时检测出已经产生的死锁,并精确地确定与死锁有关的进程和资源,然后采取适当措施清除系统中已经产生的死锁。

解除:当发现有进程死锁后,立即解脱它从死锁状态中出来。常用的方法包括剥夺资源和撤销进程。剥夺资源是从其他进程中剥夺足够数量的资源给死锁进程,以解除死锁状态。撤销进程可以直接撤销死锁进程或撤销代价最小的进程,直至有足够的资源可用,从而消除死锁状态。


                                                                                                        ---------------------懒惰的tq02

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

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

相关文章

19-求正数数组的最小不可组成和

题目 给定一个全是正数的数组arr,定义一下arr的最小不可组成和的概念: arr的所有非空子集中,把每个子集内的所有元素加起来会出现很多的值,其中最小的记为min,最大的记为max;在区间[min,max]上&#xff0…

腾讯云香港服务器租用_2核2G20M_2核4G30M

腾讯云香港服务器租用费用表,目前中国香港地域轻量应用服务器可选配置2核2G20M、2核2G30M、2核4G30M,操作系统可选Windows和Linux,不只是香港云服务器,新加坡、硅谷、法兰克福和东京服务器均有活动,腾讯云服务器网分享…

c语言每日一练(6)

前言:每日一练系列,每一期都包含5道选择题,2道编程题,博主会尽可能详细地进行讲解,令初学者也能听的清晰。每日一练系列会持续更新,暑假时三天之内必有一更,到了开学之后,将看学业情…

Stephen Wolfram:意义空间和语义运动规律

Meaning Space and Semantic Laws of Motion 意义空间和语义运动规律 We discussed above that inside ChatGPT any piece of text is effectively represented by an array of numbers that we can think of as coordinates of a point in some kind of “linguistic feature …

ShardingSphere简单介绍

此文章为笔记,为阅读其他文章的感受、补充、记录、练习、汇总,非原创,感谢每个知识分享者。 文章目录 第01章 高性能架构模式1、读写分离架构2、数据库分片架构2.1、垂直分片2.2、水平分片 3、读写分离和数据分片架构4、实现方式4.1、程序代…

智能与本体

世界的本体是一个复杂而广泛的话题,可以根据不同的学科、思想体系和信仰背景来进行不同的解释和理解。它涉及到人类对于现实和存在的思考,以及对于世界本质的追寻和探索。 在哲学上,世界的本体指的是存在的实质或基本特征。它探讨了世界的本源…

Android14操作系统全新功能发布,允许用户撤销全屏权限

最新发布的Android 14操作系统带来了一系列全新功能和改进,其中之一是新增了选项,让用户能够撤销应用的全屏权限。这样一来,用户可以阻止一些应用在全屏模式下隐藏状态栏和导航栏,从而更方便地查看时间、电量和其他信息。 此外&a…

Go项目初始化不再困扰你:gonew全方位解析

近日,Go官博介绍了一个名为gonew的新工具[1]。该工具支持基于go project template clone并创建一个属于你的Go项目。gonew工具的引入大幅简化了Go项目的创建,同时由于对自定义项目模板的支持,也可以提高Go项目的标准化水平。gonew工具刚刚被放…

Java程序猿搬砖笔记(十六)

文章目录 狂神说-Elasticsearch 7.6入门学习笔记Windows Elasticsearch IK分词器插件启动报错Elasticsearch的ik分词器自定义字典myDict.dic的编码格式需要为UTF-8,否则无效Elasticsearch使用term查询无数据返回的原因Elasticsearch如果没给映射,字段默认使用standa…

k8s node 误删除了如何自动创建 csr重新加入集群

worker node 节点当部署晚 kubelet、kube-proxy就会加入集群,如何加入呢, [rootkube-node01 ssl]# mv kubelet-client-2023-08-13-01-19-00.pem kubelet-client-current.pem kubelet.crt kubelet.key /tmp/kubelet [rootkube-node01 ssl]# systemctl da…

使用LSH 进行特征提取

局部敏感哈希(LSH)通常用于近似最近邻算法(ANN) 操作(向量搜索)。LSH的特性也可以在以矢量为输入的神经网络模型中得到利用(例如,各种的音频、视频和文本嵌入等内容信号)。 通常情况下,特定领域模型中输入的流形是复杂的(非i. i. d&#xff…

免费AI学习文档(二)

国内绘画midjourney网站 http://aijiaolian.chat优质提示词分解教学 https://q3iylvv7qj.feishu.cn/docx/UGMzdPVGjo1fHcxu1kjcuXFcnff?fromfrom_copylink设计图AI实战,如何用AI提高83%的出图效率?https://q3iylvv7qj.feishu.cn/docx/Fsxxd3MncowFUix5…

Java SpringBoot 加载 yml 配置文件中字典项

实际项目中,如果将该类信息放配置文件中的话,一般会结合Nocas一起使用 将字典数据,配置在 yml 文件中,通过加载yml将数据加载到 Map中 Spring Boot 中 yml 配置、引用其它 yml 中的配置。# 在配置文件目录(如&#xff…

windows电脑简单实时tts语音播报wsay;python pyttsx3语言实时播报text-to-speech

1、wsay 参考: https://github.com/p-groarke/wsay 下载安装: https://github.com/p-groarke/wsay/releases/tag/v1.5.0 下载exe文件,并把加入环境变量就可 使用 # Say something. wsay "Hello there."wsay "你好"…

Node.js躬行记——接口管理

在页面发生线上问题时,你要做的事情就是去查接口,响应数据是否正确,查接口的方法有两种: 第一种是在浏览器中打开地址,但是你必须得知道详细的 URL,并且有些页面还需要附带参数。 第二种是打开编辑器&#…

赴日IT培训 你到底适不适合做赴日IT?

有很多正在学习或者已经从事IT行业的小伙伴,问自己到底适不适合做赴日程序员,问什么赴日IT这么褒贬不一,自己内心很纠结。今天小编就来从多个角度为大家分析一下,希望能对徘徊、犹豫的IT工程师们有所帮助。 为什么会有一部分人犹…

九、解析应用程序——分析应用程序(2)

文章目录 一、确定服务器端功能二、解析受攻击面 一、确定服务器端功能 通过留意应用程序向客户端披露的线索,通常可推断与服务器端功能和结构有关的大量信息,或者至少可做出有根据的猜测。以下面用于访问搜索功能的URL为例: 可见,.jsp文件扩…

R语言 列表中嵌套列名一致的多个数据框如何整合为一个数据框

在批量建模后容易得到list&#xff0c;list中的每个元素都是单个的tibble 或者 dataframe&#xff0c;如何将这些数据整合为一张表呢&#xff1f; 载入R包 library(broom) library(tidyverse) 模拟数据 models <- txhousing %>% group_by(city) %>% do(modlm(lo…

ICMP协议报文

1、CMP协议简介 ICMP&#xff08;Internet Control Message Protocol&#xff09;是一种网络协议&#xff0c;它用于在IP网络中传递控制信息和错误消息。它通常与IP协议一起使用&#xff0c;IP协议负责发送和路由数据包&#xff0c;而ICMP协议负责检查网络是否可达、路由是否正…

多传感器融合相关技术

重要说明&#xff1a;本文从网上资料整理而来&#xff0c;仅记录博主学习相关知识点的过程&#xff0c;侵删。 一、参考资料 多传感器融合定位学习 深蓝-多传感器定位融合 深蓝学院 多传感器融合定位 作业 多传感器融合详解 二、相关介绍 1. 毫米波雷达&#xff08;Radar&a…