EN
/video/71535834.html

Java的深入分析 synchronized死锁:从可重入锁到哲学家用餐问题

2025-06-24 12:39:25 来源: 新华社
字号:默认 超大 | 打印 |

各位看官,早上好,午安,晚安,~~~。

假如你认为这篇文章对你有帮助。

欢迎您一键三连󿀌小编尽力做得更好。
欢迎与更多人分享。

今天我们就来学习synchronized-多线程死锁问题。

在上一节中,我们对锁定的概念有了初步的了解,通过synchronized修改代码块,将非原子操作转换为原子,那么synchronized只能修改代码块吗?#xff1f;

当然不是synchronized也可以修改;可以使用实例和静态方法)

目录。

1.修改synchronized的方法。

2:synchronized是一种可重入锁。

3:死锁。

3.1.引入死锁。

3.2.哲学家的饮食问题。


首先,补充知识(因为小编一直很着迷)

1.synchronized修改方法。

synchronized public void increase(){     // synchronized修改实例方法        count++;    }    public void increase1(){         synchronized (this){    // 使用this作为锁对象            count++;        }    }        synchronized public static void increase2(){         count++;    }    public void increase3(){         synchronized (Demo1.class){   // 通过反射获得这个类对象            count++;        }    }。

一个.编译java文件 => .class(字节码文件) => 运行时.将class文件加载到JVM中,即:JVM加载到内存的数据结构是类对象)

说明:

加载一个JVM.class文件时,它将在内存中创建相应的数据结构,该数据结构通常被称为“类对象”或“类结构”。一个.编译java文件 => .class(字节码文件) => 运行时.将class文件加载到JVM中,即:JVM加载到内存的数据结构是类对象)

说明:

加载一个JVM.class文件时,它将在内存中创建相应的数据结构,

这种数据结构通常被称为“类对象”或“类结构”。这个类对象包含了类中定义的所有信息。

类别对象包括:属性,名字,类型,权限,类别方法,继承哪个类别󿼌实现了哪个接口...2:synchronized是一种可重入锁。可重新入锁是什么意思?一个线程。连续两次加锁一个对象。

不会出现。

死锁(我们会详细说明死锁)

。满足这一要求的是可重入锁。

我们以代码为例:

synchronized (locker){ synchronized (locker){ count++; } }。

合理地说,这个代码卡住了:

说明:  第一次给locker加锁#xfff0c;按理说,下面的第二次加锁操作肯定会堵塞,等待第一次加锁释放锁#xff0c;我这个synchrinized可以给locker加锁。但是第二次不加锁,第一次加锁就解锁不了。这完全死了(这种情况会死锁)

但是!!!synchronized可重入锁:也就是说,一个对象可以连续加锁两次。对象头部将有一个计数器(这个线程锁定了这个对象,计数器+1)解释对象头和计数器#xff0c;给一个对象一定会保存这个线程的信息。加锁时,对象将保存此线程的信息,这些信息保存在对象头(隐藏的信息)的Mark 在word中。

每个对象都有一个对象头(Object Header),它包含了对象的一些元数据(例如,锁状态)

。并且一个线程连续锁定一个对象,还会有一个计数器󿀌

这个计数器也存在于对象头中的Mark Word中。该机制确保同一线程可以多次获得同一对象的锁。

解锁时:

当一个对象被加入三把锁时,加入计数器的值 = 3 ,解锁不是真的解锁,而是解锁一个锁计数器-1,直到减到0才是真正的解锁。

3:死锁。

3.1.引入死锁。

刚才我们讲述了synchronized是一个可重入锁,连续锁定一个对象不会导致死锁(线程卡死)。那么在什么情况下会出现死锁࿱?f;

接下来给大家看一个代码。

public static void main(String[] args) { Object locker1 = new Object(); Object locker2 = new Object(); Thread t1 = new Thread(() ->{ synchronized (locker1){ try { Thread.sleep(1000); ///这个休眠1s真的很重要c;打开t1线程,立即得到它 locker1这把锁,若不休眠 ///这个线程很容易一下子拿到两个锁。 } catch (InterruptedException e) { throw new RuntimeException(e); } // 嵌套的锁 synchronized (locker2){ System.out.println("t1成功加锁;); } } }); Thread t2 = new Thread(() -> { synchronized(locker2){ try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } // 嵌套的锁 synchronized(locker1){ System.out.println("t2 成功加锁"); } } }); t1.start(); t2.start(); }。

结果:什么都没打印。

结果:什么都没打印。让我们进入jconsole,看看两个线程的状态。

画个图解释一下。所以说:

因此,两个线程都没有成功获得第二把锁。

 。

这属于嵌套关系,线程A得到locker1的锁,想要locker2这把锁。

(死锁)可能会出现;但如果是并列关系(线程A先释放前面的锁,再次获得下一个锁)(不会死锁)

如何将嵌套变成并列࿱?f;改变代码结构!

这样就解决了,但有时代码结构不太容易改变。

刚才说死锁形成环。。。

哲学家用餐会有一个经典的问题。

3.2.哲学家用餐问题。

先做一张图来表示这个问题。

这就是导致死锁问题(如何解决)

说到如何解决󿀌要知道形成死锁的几个必要条件,我们破坏其中一个就可以了。

有四个条件(前两个条件是synchronized的属性(我们不能改变))

1.互斥使用(锁的基本特性):一个线程有锁A,如果你想得到另一个线程,你只能阻止等待。

2.不要抢占(锁的基本特性):和条件差不多,另一个线程只能等待那个线程释放锁A,无法抢占。

3.保持请求(#xfff09代码结构;:一个线程想要获得多个锁#xff08;嵌套,想锁B但又不想释放自己的锁B)(其实并没有形成环󿀌想要几个锁都没问题,#xff09;

4.循环等待(#xfff09代码结构;:(条件3导致条件4),等待的关系形成了一个环。

条件3:事实上,有时需要获得更多的锁,这种结构很难改变。

条件4:我们同意了加锁的顺序,避免循环等待(为锁编号,先加锁再加锁#xff09;

所以哲学家就餐问题可以这样解决!!!

我们可以规定,每个哲学家只能先拿起数字小的筷子,哲学家B先拿起筷子,#xff00c;然后哲学家A面前只剩下一根筷子5,但是他现在不能拿(这个数字比较大󿂙所以哲学家E可以吃面条,然后就通了。
以上是synchronized-死锁问题。

所有内容都是,死锁的出现,它将使我们的程序陷入死循环问题,但我们只需要知道死锁的原因,至少我知道如何解决这个问题~~~如何预测后事󿀌下次请听分解~~。

看这里相信你一定对小编的文章有一定的认可。欢迎各位大佬指出任何问题。欢迎您在评论区留言修改~~。你的支持是我最大的动力!!!

【我要纠错】责任编辑:新华社