【synchronized底层原理之2】悲观锁与乐观锁、线程阻塞的代价等

悲观锁与乐观锁

悲观锁(Pessimistic Lock)

悲观锁是就是悲观思想,即认为写多,遇到并发写的可能性高,每次去拿数据的时候都认为别人会修改,所以每次在读写数据的时候都会上锁,这样别人想读写这个数据就会block直到拿到锁。由于数据进行加锁,期间对该数据进行读写的其他线程都会进行等待。

synchronized的重量级锁就是悲观锁。AQS框架下的锁则是先尝试cas乐观锁去获取锁,获取不到,才会转换为悲观锁,如RetreenLock。

乐观锁(Optimistic Lock)

乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,采取在写时先读出当前版本号,然后加锁操作(比较跟上一次的版本号,如果一样则更新),如果失败则要重复读-比较-写的操作。由于数据没有进行加锁,期间该数据可以被其他线程进行读写操作。

java中的乐观锁基本都是通过CAS操作实现的,CAS是一种更新的原子操作,比较当前值跟传入值是否一样,一样则更新,否则失败。

synchronized的偏向锁和轻量级锁就是乐观锁

★小结

在Java技术发展史上,最开始使用的是悲观锁,实践中发现使用悲观锁有很多缺点,所以又引入了乐观锁。

两种锁各有优缺点,读取频繁使用乐观锁(但是可能出现“脏”读),写入频繁使用悲观锁(上下文切换开销大,但是保证没有“脏”数据)。

synchronized的重量级锁就是悲观锁

synchronized的偏向锁和轻量级锁就是乐观锁

java线程阻塞的代价--主要是上下文切换

Java线程的上下文切换需要操作系统的介入

java的线程是映射到操作系统原生线程之上的,如果要阻塞或唤醒一个线程就需要操作系统介入。

操作系统切换线程需要在用户态与内核态之间切换:因为用户态和内核态内存空间各自独立,故而切换要传递变量和参数,这样的话系统消耗较大,费时

需要在用户态与内核态之间切换,这种切换会消耗大量的系统资源,因为用户态与内核态都有各自专用的内存空间,专用的寄存器等,用户态切换至内核态需要传递给许多变量、参数给内核,内核也需要保护好用户态在切换时的一些寄存器值、变量等,以便内核态调用结束后切换回用户态继续工作。

频繁的上下文切换很费时,如果同步代码执行所需时间比上下文切换时间都要短,那引入重量级锁切换上下文这种同步策略是失败的:所以synchronized从JKD1.6进行了改进,引入了偏向锁、轻量级锁

java对象头中的markword

markword是java对象数据结构中的一部分,对象的markword和java各种类型的锁密切相关。

markword数据的长度在32位和64位的虚拟机(未开启压缩指针)中分别为32bit和64bit,它的最后2bit是锁状态标志位,用来标记当前对象的状态,对象的所处的状态,决定了markword存储的内容,如下表所示

32位虚拟机在不同状态下markword结构如下图所示

了解了markword结构,有助于了解java锁的加锁解锁过程。

转载于:https://my.oschina.net/u/3866531/blog/2046372

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值