Java面试经典基础问题 锁和死锁的理解

首先  Java中  synchronized 是可重入锁

为什么要锁?

如下代码 我们原本想要得到的结果是count = 10000,但是真正输出的确是不稳定的结果

出现这种情况明显是代码出BUG了 。 这里我们分析一下:

首先我们要明白这此处相加的过程中其实分为三个步骤:

1:load  从内存中读取值到寄存器中

2:add  在寄存器中数据加 1

3:save将在寄存器中相加的结果再保存到内存中

此时我们2个线程都在进行数据的相加   由于他们针对的是同一对象(即count)且彼此是相互独立的,再加上线程调度是随机的 最终就会出现如下现象:

出现这种情况最主要是一下3个原因:

1:线程调度是随机的 即 load ,add ,save 这三个操作单一线程是正常走,但从2个线程看则是完全随机的比如A进行load  B则可能已经在save了。

2:这两个线程是针对同一变量在修改。

3:修改操作不是原子的,即修改操作是需要进行load,add,save 3个步骤一个一个全部完成后才算完成,如果是原子的意思就是一步就能实现这3个操作的意思。

此时我们的解决方法就是加锁。

在Java中最常用的方法就是使用synchronized关键字。

关于死锁 如果是不可重入锁 那就是当一个线程针对一个对象连续两次加锁,彼此等待就形成了死锁。

synchronized是可重入锁则不会死锁:

而换一种情况即 t1获取到锁 jocker1 ,t2获取到锁 jocker2 后,t1又想获取 jocker2,t2又想获取jocker1。双方此时都不愿意先放开锁,此时就会僵持住就会形成死锁。

而如果像 synchronized 这种可重入锁 系统在识别到是同一个线程同一对象加锁时,就会直接加锁成功,不同对象则谁后来则谁会形成阻塞等待。这样避免了死锁的产生,可以说非常友好,同时对于加了多重锁的情况什么时候真正解锁,则其内置计数器 加一次锁计数就加一,每解锁一次计数器就减一,直到减到0的时候就真正解锁完成。

死锁的成因,主要由一下四个必要条件:

1:互斥使用:(锁本身的特性)当一个线程把锁上锁成功后 ,别的线程也想加锁则需要阻塞等待。

2:   不可抢占:(锁本身的特性)当锁被线程1拿到之后,线程2只能等线程1适当后才能加锁,无法强制抢夺过来。

3:请求保持:(代码结构)一个线程尝试获取多把锁(一个线程拿到锁1后,又想获取锁2,同时又不放开锁1)(吃这碗里,看着锅里的)

4:循环等待/环路等待: (车钥匙放家了,家门钥匙锁车里了)

如下图:

对于解决方法:

问题1和2 是本身特性无法解决,问题3的解决方法是 尽量避免在代码中使用嵌套加锁,比如1包2这种。    如无法避免即问题4这种情况  则约定好顺序统一先加小的再加大的。即如下图:

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值