synchronized的总结

文章详细介绍了synchronized的各种锁机制,包括乐观锁与悲观锁的概念,轻量级锁和重量级锁的区别,自旋锁与挂起等待锁的工作原理,以及互斥锁和读写锁的适用场景。同时,文章讨论了可重入锁和不可重入锁的特性,并通过案例分析了死锁的三种情况。此外,还提到了公平锁与非公平锁的定义。最后指出synchronized既是乐观锁也是悲观锁,既是轻量级锁也是重量级锁,并具有非公平性。
摘要由CSDN通过智能技术生成

🔎乐观锁与悲观锁

锁🔒冲突
多个线程针对同一个对象进行加锁🔒,可能产生阻塞等待

乐观锁🔒

预测接下来锁🔒冲突的概率不大
乐观锁总是假设最好的情况,认为一般情况下不会产生并发冲突,所以只在数据进行提交修改的时候去对数据进行冲突检测

悲观锁🔒

预测接下来锁🔒冲突的概率较大
悲观锁总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据时都会上锁


🔎轻量级锁与重量级锁

轻量级锁🔒

加锁解锁的过程更快更高效

重量级锁🔒

加锁解锁的过程更慢更低效

注意
一个乐观锁🔒很可能也是一个轻量级锁🔒(不绝对)
一个悲观锁🔒很可能也是一个重量级锁🔒(不绝对)


🔎自旋锁与挂起等待锁

自旋锁🔒是轻量级锁的一种典型实现
挂起等待锁🔒是重量级锁的一种典型实现

举个栗子🥝

有一个老哥去追他喜欢的女神
问女神,你能做我女朋友吗
女神说:对不起,你是个好人
(老哥对女神加锁失败)

在这里插入图片描述

自旋锁🔒

这时候老哥选择继续每天向女神问候早安,午安,晚安
一旦哪一天,女神和她男朋友分手了
那老哥的机会就来了(有很大的机会加锁🔒成功)

自旋锁的意思就是一旦锁🔒被释放,就能第一时间拿到锁,速度更快
缺点是每天都得围着女神转(等待加锁),不能去做其他的事情(忙等,消耗cpu)

注意
自旋锁通常是纯用户态的,不需要经过内核态,时间相对较长

挂起等待锁🔒

这时候老哥选择果断放弃,潜心敲代码
等到哪一天女神分手(这中间不知道分手了多少个),最后找到了老哥求安慰,老哥再次尝试加锁

挂起等待锁的意思是锁🔒被释放,不能第一时间拿到锁,可能要很久才能拿到锁
优点是时间是空闲出来的(不用忙等,消耗cpu)

注意
挂起等待锁通过内核的机制(内核态)实现挂起等待,时间相对较长


🔎互斥锁与读写锁

互斥锁🔒

synchronized 只有两个操作
(1)进代码块,加锁
(2)出代码块,解锁

synchronized 是互斥锁🔒
加锁,就只是单纯的加锁,没由更进一步的划分了

读写锁🔒

(1)给读加锁
(2)给写加锁
(3)解锁

读写锁中约定
(1)读锁与读锁之间,不会产生锁竞争(不会影响程序速度)
(2)写锁与写锁之间,会产生锁竞争(会降低程序速度,但保证数据准确性)
(3)读锁与写锁之间,会产生锁竞争(会降低程序速度,但保证数据准确性)

注意
读写锁更适合于频繁读不频繁写的情况

Java标准库中提供了 ReentrantReadWriteLock 类,实现了读写锁
(1) ReentrantReadWriteLock.readLock 表示读一个锁
(2) ReentrantReadWriteLock.writeLock 表示写一个锁


🔎可重入锁与不可重入锁

可重入锁🔒

一个线程针对一把锁,连续加锁两次,没出现死锁,就是可重入锁🔒

不可重入锁🔒

一个线程针对一把锁,连续加锁两次,出现死锁,就是不可重入锁🔒

死锁🔒

举个栗子🥝
车钥匙忘在家里
家钥匙忘在车里
如果想拿到车钥匙就得先进入家里
如果想进入家里就得先拿到车钥匙

关于死锁🔒

case1🥝 一个线程一把锁
private static Object locker = new Object();
    
public static void main(String[] args) {
        synchronized (locker) {
            synchronized (locker) {

            }
        }
}

形如这样的代码,就是一个线程连续对一把锁加锁两次

加锁的时候判定一下看当前申请锁的线程是不是锁的拥有者(第一次加锁的线程)

如果是
第二次加锁成功(可重入锁🔒)

如果不是
第二次加锁需要等待第一次加锁的释放,第一次加锁的释放需要等待第二次加锁的成功
这样就形成了死锁(不可重入锁🔒)


case2🥝 两个线程两把锁
public class Test {

    private static Object locker1 = new Object();
    private static Object locker2 = new Object();

    static class Conuter {
        public static int count = 0;

        public void add1() {
            synchronized (locker1) {
                synchronized (locker2) {
                    count++;
                }
            }
        }

        public void add2() {
            synchronized (locker2) {
                synchronized (locker1) {
                    count++;
                }
            }
        }

    }

}

形如这样的代码,就是两个线程对两把锁进行加锁

线程1执行add1()方法,对 locker1进行加锁
此时线程2执行add1()方法,发现add1()方法被线程1加锁,只能等待

线程2执行add2()方法,对 locker2进行加锁
此时线程1执行add2()方法,发现add2()方法被线程2加锁,只能等待

由于add1()方法里面还需一个 locker2加锁,故线程1不能执行add1()方法
由于add2()方法里面还需一个 locker1加锁,故线程2不能执行add2()方法

但是线程2对locker2加锁需要等待线程1将 locker1释放
但是线程1对locker1加锁需要等待线程2将 locker2释放

于是就形成了死锁


case3🥝 M个线程N把锁(哲学家就餐问题)

当线程和锁的数量更多时,就更容易发生死锁问题了

举个栗子🥝

假设有5位哲学家
(1)每个人随机的进行吃面条(拿起筷子)和思考人生(放下筷子)
(2)这5位哲学家比较固执(如果他想拿筷子,却被别人占用了,就会进行等待,等待的过程中也不会放下手中的筷子)

注意
哲学家 ≈ 线程
筷子 ≈ 锁

在这里插入图片描述

假设5位哲学家同时拿起各自左手边的筷子,那么此时就会发生死锁🔒的情况(每个人都吃不到面条)

解决方案🌸
为筷子(锁)加编号
如果需要同时获取多把锁,约定加锁顺序(从编号小的开始加锁)

在这里插入图片描述
为了避免死锁的情况,给筷子(锁)加了编号
规定每个人先从编号小的筷子拿起,再拿编号大的筷子
于是
2号哲学家拿了筷子1
3号哲学家拿了筷子2
4号哲学家拿了筷子3
5号哲学家拿了筷子4
由于此时1号哲学家没能拿到编号较小的筷子1,所以他也不能拿到筷子5
这时5号哲学家拿起了筷子5,吃完后放下筷子(思考人生)
4号哲学家等5号哲学家放下筷子4,成功拿起筷子4,再结合手中的筷子3,顺利吃上面条
3号哲学家等4号哲学家放下筷子3,成功拿起筷子3,再结合手中的筷子2,顺利吃上面条

哲学家就餐问题就被完美解决了

对死锁🔒的总结

死锁的形成有四个必要条件
(1)互斥使用
一个线程拿到一把锁之后,另一个线程不能使用
(2)不可抢占
一个线程拿到锁之后,只能自己释放,其他线程不能强行占有(挖墙脚行为不道德)
(3)请求和保持
一个线程拿到一把锁之后(保持),继续申请其他的锁(请求)
一位哲学家拿到一根筷子后,不放下,继续申请其他的筷子
(4)循环等待
不戴口罩不能进药店,进药店才能买口罩戴


🔎公平锁与非公平锁

公平锁🔒

遵守先来后到

非公平锁🔒

不遵守先来后到

举个栗子🥝

一群老哥追一个女神
老哥A:我认识了一年
老哥B:我认识了5个月
老哥C:我认识了1个月

在这里插入图片描述

公平锁🔒
老哥A先来的,让老哥A先去追,
在这里插入图片描述

非公平锁🔒
凭啥你先来的你就能先追
大家各凭本事,谁追到算谁的
此处虽然是等概率竞争,但并未遵循先来后到原则,所以等概率也就成了非公平

在这里插入图片描述

🔎synchronized的特点

(1)既是乐观锁,也是悲观锁
(2)既是轻量级锁,也是重量级锁
(3)轻量级锁基于自旋锁实现,重量级锁基于挂起等待锁实现
(4)是互斥锁,不是读写锁
(5)是可重入锁
(6)是非公平锁

🔎结尾

创作不易,如果对您有帮助,希望您能点个免费的赞👍
大家有什么不太理解的,可以私信或者评论区留言,一起加油

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值