【JavaEE】——多重锁,死锁问题和解决思路

简介: 加锁的“可重入性”,死锁,产生死锁的四个必要条件,避免死锁的解决思路

 阿华代码,不是逆风,就是我疯,你们的点赞收藏是我前进最大的动力!!希望本文内容能够帮助到你!

目录

一:加锁的“可重入性”

1:问题引入

2:问题分析

3:可重入性

(1)解释

(2)应用举例

4:内层机制分析

二:死锁

1:情景一

(1)代码解析

2:情景二——N个线程M把锁(进阶)

(1)举例:哲学家吃面条问题

(2)极端情况

(3)产生死锁的四个必要条件

①互斥性

②不可抢占性

③保持性

④循环等待

三:解锁

1:解锁思路

2:指定加锁顺序


引入:通过上一篇文章的学习,我们针对线程安全问题,简单认识了synchronized关键字,给操作“打包”,避免了一些多线程会出现的bug,通过本章学习我们将更进一步的学习Synchronized

一:加锁的“可重入性”

1:问题引入

我们给Thread线程,加两道锁,提问:hello能顺利打印出来吗

public class ThreadDemon24 {
    public static void main(String[] args) {
        Object locker = new Object();
        Thread t1 = new Thread(()->{
            synchronized(locker){
                synchronized(locker){
                    System.out.println("hello");
                }
            }
        });
        t1.start();
    }
}

image.gif

image.gif 编辑

2:问题分析

问:2已经加锁了,1再尝试加锁,不会出现“阻塞”情况吗?觉得会阻塞的扣1,不会阻塞的扣2~~

image.gif 编辑

恭喜扣2的同学。由于是同一个线程,代码走到1时第一次加锁,走到2时,被加锁的对象locker知道第二次加锁是同一个线程(友军),所以就放行了,不会“阻塞”。

3:可重入性

(1)解释

“可重入性”就是在加锁机制中,同一个对象在同一个线程下第一次加完锁之后,再次遇到该线程下的对该对象的加锁,此时第二次加锁操作就会直接对该对象放行。就不会出现“阻塞”和“死锁”的情况。

(2)应用举例

image.gif 编辑

我们平时写的代码,由于复杂的层层调用,无意中写出了双重加锁(锁重复)的情况,此时加锁的“可重复性”机制就能很好的解决这个问题。

4:内层机制分析

对于“可重入锁”机制来说,内部有两个信息

①当前“锁”是被哪个线程所持有的

②加锁次数的计数器

最外层的   {   这种括号负责加锁

最外层的   }  这种括号负责解锁

image.gif 编辑

注:锁状态是没法直接查看的,我们只能通过jconsole来查看线程的状态,比如:BLOCKED

二:死锁

1:情景一

image.gif 编辑

image.gif 编辑

package thread;
/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: Hua YY
 * Date: 2024-09-22
 * Time: 10:32
 */
public class ThreadDemon25 {
    public static void main(String[] args) {
        Object A = new Object();
        Object B = new Object();
        Thread t1 = new Thread(()->{
            synchronized(A){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized(B){
                    System.out.println("尝试获取B锁");
                }
            }
        });
        Thread t2 = new Thread(()->{
            synchronized(B){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized(A){
                    System.out.println("尝试获取A锁");
                }
            }
        });
        t1.start();
        t2.start();
    }
}

image.gif

(1)代码解析

通过 上面的代码结果的分析,t1和t2线程堵塞了,即t1想要获取B这个对象加锁,但是B已经被t2给锁上了,t2想要获取A这个对象,但是A已经被t1给锁上了。

2:情景二——N个线程M把锁(进阶)

(1)举例:哲学家吃面条问题

image.gif 编辑

(五个滑稽老铁相当于五个线程,五根筷子相当于五把锁)有五个滑稽老铁一起吃面条,每个滑稽间放了一根筷子,比如一号滑稽老铁想要吃面条,就必须同时拿起1、5两根筷子,滑稽老铁吃面条的时候,其它人不能硬抢筷子。

滑稽老铁除了吃面条,就是放下筷子思考人生(线程不工作),由于每位哲学家什么时候吃面条,什么时候思考人生是不确定的(线程的随机调度),所以大部分情况下,筷子是够用的。

(2)极端情况

所有人都想吃面条,同时拿起左手边的筷子,此时想拿右手边的筷子时就g了(没人吃到面,也没人释放筷子,这就成了一个死锁了)

(3)产生死锁的四个必要条件

①互斥性

获取锁的过程是互斥的,一把锁只能被一个线程获取,另一个线程想要获取同一把锁就必须阻塞等待

②不可抢占性

一个线程拿到了锁,除非这个线程主动解锁,否则不会被别的线程强行把锁给抢走

③保持性

一个线程想获取第二把锁,那么第一把锁依旧还是存在的,不会消失

④循环等待

三:解锁

1:解锁思路

只要破坏掉上述产生死锁的四个必要条件中的随便一个,我们就能解锁了

但是①②点都是锁的最基本特性,不好破坏,③得具体情况具体分析,④破坏这种循坏的代码结构是最容易的。

2:指定加锁顺序

image.gif 编辑

针对五把锁进行编号,五个滑稽老铁进行编号,指定一定的规则:先获取编号小的锁,在获取编号大的锁,这样就避免循环等待,避免了死锁。

当然这里还有很多别的方法就不一一赘述了~~


相关文章
|
4月前
|
Java
什么是 CAS(自旋锁)? 它的优缺点? 如何使用CAS实现一把锁?
该博客文章解释了什么是CAS(自旋锁),包括CAS的基本概念、实现原理、优缺点,以及如何使用CAS实现锁的逻辑,并提供了使用CAS实现锁的Java完整代码示例和测试结果。
什么是 CAS(自旋锁)? 它的优缺点? 如何使用CAS实现一把锁?
|
6月前
|
Java
探秘死锁:原理、发生条件及解决方案
探秘死锁:原理、发生条件及解决方案
154 1
|
6月前
|
Python
Python多线程中递归锁如何解决死锁问题的详细阐述
Python多线程中递归锁如何解决死锁问题的详细阐述
|
6月前
|
算法 Java 开发者
深入理解死锁的原因、表现形式以及解决方法,对于提高Java并发编程的效率和安全性具有重要意义
【6月更文挑战第10天】本文探讨了Java并发编程中的死锁问题,包括死锁的基本概念、产生原因和解决策略。死锁是因线程间争夺资源导致的互相等待现象,常由互斥、请求与保持、非剥夺和循环等待条件引起。常见死锁场景包括资源请求顺序不一致、循环等待等。解决死锁的方法包括避免嵌套锁、设置锁获取超时、规定锁顺序、检测与恢复死锁,以及使用高级并发工具。理解并防止死锁有助于提升Java并发编程的效率和系统稳定性。
416 0
|
7月前
|
算法 Java 编译器
【JavaEE多线程】掌握锁策略与预防死锁
【JavaEE多线程】掌握锁策略与预防死锁
69 2
|
7月前
|
安全 算法 Java
Java多线程基础-15:Java 中 synchronized 的优化操作 -- 锁升级、锁消除、锁粗化
`synchronized`在Java并发编程中具有以下特性:开始时是乐观锁,竞争激烈时转为悲观锁;从轻量级锁升级至重量级锁;常使用自旋锁策略;是不公平且可重入的;不支持读写锁。
59 0
|
安全 算法 Java
可重入锁,不可重入锁,死锁的多种情况,以及产生的原因,如何解决,synchronized采用的锁策略(渣女圣经)自适应的底层,锁清除,锁粗化,CAS的部分应用
可重入锁,不可重入锁,死锁的多种情况,以及产生的原因,如何解决,synchronized采用的锁策略(渣女圣经)自适应的底层,锁清除,锁粗化,CAS的部分应用
|
7月前
|
安全 Java 程序员
【Java多线程】面试常考——锁策略、synchronized的锁升级优化过程以及CAS(Compare and swap)
【Java多线程】面试常考——锁策略、synchronized的锁升级优化过程以及CAS(Compare and swap)
70 0
|
7月前
|
Java
【专栏】Java多线程中,锁用于控制共享资源访问,确保数据一致性和正确性,锁是什么意思,有哪些分类?
【4月更文挑战第28天】Java多线程中,锁用于控制共享资源访问,确保数据一致性和正确性。本文探讨锁的概念、作用及分类:乐观锁与悲观锁、自旋锁与适应性自旋锁、公平锁与非公平锁、可重入锁和读写锁。使用锁需注意避免死锁、合理选择锁粒度及性能优化。理解锁有助于提升多线程编程的效率和稳定性。
112 0
|
7月前
|
Java Linux 程序员
【JavaEE初阶】 常见的锁策略详解
【JavaEE初阶】 常见的锁策略详解

热门文章

最新文章