synchronized 的特性与机制

目录

1.synchronized的特性

锁策略

(1) 既是乐观锁也是悲观锁

(2) 既是轻量级锁,也是重量级锁

(3) 轻量级锁基于自旋实现,重量级锁基于挂起等待实现

(4)不是读写锁

(5)是可重入锁

(6) 是非公平锁

2.synchronized的使用

3.synchronized的锁机制

偏向锁 

自旋锁和重量级锁

4.synchronized的优化策略

1.锁的消除

2.锁的粗化


1.synchronized的特性

        (1) 既是乐观锁也是悲观锁

        (2) 既是轻量级锁,也是重量级锁

        (3) 轻量级锁基于自旋实现,重量级锁基于挂起等待实现

        (4)不是读写锁

        (5)是可重入锁

        (6) 是非公平锁

先给出特点逐一解释每一个代表什么意思

首先引入一个锁策略,接下来会讲述不同锁策略的实现

锁策略

        悲观锁 vs 乐观锁

        轻量级锁 vs 重量级锁

        自旋锁 vs 挂起等待锁

        互斥锁 vs 读写锁

        可重入锁 vs 不可重入锁

        公平锁 vs 非公平锁

接下来会一一介绍

(1) 既是乐观锁也是悲观锁

        就是锁的实现预测接下锁的冲突(比如说有两个或者七八个竞争锁),根据冲突的概率大小来决定之后要做什么

        乐观锁 : 预测接下来冲突概率不大

        悲观锁 : 预测接下来冲突概率比较大

        两种锁会导致之后要做的事情是不一样的

        通常来说悲观锁做的工作会多一些,效率会更低

        乐观锁做的工作会少一下,效率会更高(并不绝对)

(2) 既是轻量级锁,也是重量级锁

        (与乐观锁悲观锁有一定的重合)

        轻量级锁的加锁解锁过程更快更高效

        重量级锁的加锁解锁过程更慢更低效

        一个乐观锁很可能也是一个轻量级锁(不绝对)

        一个悲观锁很可能也是一个重量级锁(不绝对)

(3) 轻量级锁基于自旋实现,重量级锁基于挂起等待实现

        轻量级锁是通常是纯用户态的不需要经过内核态(时间相对更短)

        重量级锁是通过内核的机制来实现挂起等待(时间会更长)

        自旋锁是一旦锁被释放后,就能第一时间拿到锁,但是干不了别的事情会进入忙等状态消耗cpu资源

        挂起等待锁是如果锁被释放了,不能第一时间拿到锁,可能过很久才有可能拿到锁

(4)不是读写锁

        synchronized是互斥锁(就是加锁只有单纯的加锁)

        读写锁分成

                1.给读加锁        

                2.给写加锁       

                3.解锁

        读写锁规定

                1.读锁与读锁之间不会发生锁竞争,不会产生阻塞等待

                2.写锁与写锁会发生锁竞争

                3.读锁与写锁之间也有锁竞争

(5)是可重入锁

        如果一个锁在一个线程中对该锁加锁了两次不死锁,就叫做可重入锁

        反之,产生死锁就叫做不可重入锁

synchronized(locker){
    synchronized(locker){
        
    
    }

}

比如这种情况,在Java中并不会产生死锁

在加锁的时候会进行判定,看一下当前申请的锁的线程是不是锁的拥有者,如果是的话直接放行

(6) 是非公平锁

        遵守先来后到的就是公平锁

        不遵守先来后到的就是非公平锁(等概率竞争)

        想要实现先来后到需要使用队列来记录线程的顺序才可以

2.synchronized的使用

        之前的一篇已经写过了可以移步至Java线程不安全的原因与方案-CSDN博客

3.synchronized的锁机制

        无锁 -> 偏向锁 -> 自旋锁(轻量级锁) -> 重量级锁

        概括 : 刚开始加锁是偏向锁状态,遇到了锁竞争,就变成自旋锁,竞争愈演愈烈(七八个争一个锁)直接变成重量级锁(交给内核阻塞等待)

偏向锁 

        先让线程对这个锁相当于打个标记(做标记非常快,切非常轻量)

        如果代码执行过程中都没有遇到其他线程与我争夺这个锁,此时就不需要加锁了

        一旦遇到了别的线程来竞争这个锁,这个偏向锁立马升级成轻量级锁,别的线程只能等待(jvm会通知先到的线程,让他把锁进行升级一下)

        这里的轻量级锁是基于后面cas来实现的之后一篇博客会提到,这里先不叙述

        既可以保证效率,也可以保证线程安全

自旋锁和重量级锁

        自旋锁虽然可以在第一时间拿到锁,但是消耗大量的cpu资源,如果七八个自选,会大量占用cpu的资源,所以会转化成重量级锁,在内核进行阻塞等待

4.synchronized的优化策略

1.锁的消除

非必要不加锁

        在编译阶段会进行优化,检查当前代码是否是多线程执行是否有必要进行加锁,如果无必要,又把锁写了,编译过程中会自动帮你把锁去掉

        举个例子

        StringBuilder和StringBuffer

        StringBuffer 把关键方法都加上了synchronized关键字,但是如果单线程使用StringBuffer不涉及到线程安全的问题,这个并不会被编译,也就是加锁操作不会被编译

2.锁的粗化

        锁的粒度,synchronized代码块,包含代码的多少(代码越多,粒度越粗,代码越少,粒度越细)

        一般写代码的时候,大多数情况下希望粒度更小一点(串行代码上,并发执行的代码就多)

但是如果有个场景要频繁加解锁的话,如图

         此时编译器就有可能将这个操作换成更粗粒度的锁

         由于每次加锁解锁都会产生开销(尤其是释放锁后,重新加锁,需要重新竞争)

        并且每次锁竞争都可能引入一定的等待开销. 此时整体的效率可能反而更低!!(比如自旋)

        所以编译器和jvm会自动帮你进行优化

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值