synchronized加锁策略与CAS

在进行多线程程序的编写时为了避免一些线程的安全问题,那么就离不开一个关键字synchronized。该关键字可以保证操作的原子性且保证了内存的可见性,从而保证多线程的执行的安全。当然我们大家都知道synchronized是一种锁,但是其实它却又不知是一种锁,其实synchronized是可以根据程序运行的不同情况来决定自身的锁策略。

#加锁的过程

JVM 将 synchronized 锁分为 无锁、偏向锁、轻量级锁、重量级锁。会根据情况,进行依次升级。

1.偏向锁

当我们在对一段代码使用synchronized时其实并不是直接就对其加锁,而是做了一个‘偏向锁的标记’,也就是说偏向锁其实并没有对对象真正的加锁。只有当有其他线程来竞争该锁时其才会真正的对其上锁,所以说偏向锁的本质是一种‘延迟加锁’能不加锁就不加锁,这样做就可以避免一些不必要的加锁与解锁操作,提高了效率。

2.轻量级锁

当加锁成功后,线程中虽然存在锁竞争但是并不强烈此时就处于一种轻量级锁的状态。轻量级锁的实现依赖用于CAS,同时其也是自适应自旋锁。自旋锁就是在某一线程等待锁时会不断的询问该锁是否已经释放,会导致CPU不断的空转但同时也会在锁释放的瞬间以最快的速度获得锁,而自适应自旋锁则是询问一段时间后如果锁还没被释放则放弃。所以说轻量级锁是在锁冲突不明显的时候出现的,如果锁冲突严重那么频繁的使CPU空转是非常消耗资源的。

3.重量级锁

那么当线程中的锁冲突开始比较频繁的出现时,此时轻量级锁就会膨胀成重量级锁。其依赖于每个对象内部都有的monitor锁来实现的,而monitor又依赖于操作系统的MutexLock(互斥锁)来实现,所以一般重量级锁也叫互斥锁。由于需要在操作系统的内核态和用户态之间切换的,会将线程阻塞挂起,切换线程的上下文,再恢复等操作,所以当synchronized升级成互斥锁,依赖monitor的时候,开销就比较大了(因为我们普遍认为在用户态的执行速度是大于内核态的),因为重量级锁本质上需要通过内核态来实现操作系统中的互斥锁又进入内核态会将其他线程挂机等待所以所其不可避免的是一种笨重的锁是比较耗费时间与资源的。

#CAS策略

我们在上述描写轻量级锁的时候提到了其实现依赖与CAS,那么什么是CAS?CAS全称为“Change  And Swap”,为“改变与交换”,我们在多线程中出现的线程安全的问题多数都是多个线程修改同一变量而导致变量的值变得不确定,那么CAS策略是如何保证线程的安全?

 就是说当使用CAS时每当我们要将修改过的变量放回内存时都要将开始时读取内存的值与当前内存中存放的值做比较,如果值不相等那么则说明此时内存中的值已被其他线程修改了,那么就重新读取,如果值相等才能存入内存。

#CAS导致ABA问题

虽然CAS可以解决上述提到的安全问题,当时它也有可能会带来一些问题,就好比此时提到的ABA问题。所谓ABA问题打个比方,假如你往银行卡里存了100,此时你想要取出50,那么如果此时是在多线程环境下,则可能会又两个线程来执行这个任务。假如线程1此时已经扣去50并将其放回内存此时内存中为50,如果此时线程2扣去50后想将其放回内存中是不允许的,因为我们读取的原始内存值为100此时为50不相等。但是如果出现一种极端情况就是此时有人往你卡中有打了50,此时内存中又是100,这就导致了线程2会从内存中在扣去50,将50在放回内存,这就导致了2次扣除,出现了线程的安全问题。那么如何解决这个问题呢?也很简单,就是对读取的内存初始值加上一个版本号,只有当值与版本号都相同时才允许放会内存。在上述例子中,如果引入版本号,那么线程2取得的内存初值100的版本号为1,而打入50将内存值变为100的版本号为3,则此时线程2就不能把50在放回内存中,解决了ABA问题。

#其他加锁的优化策略

1.锁消除

一些应用程序的代码中虽然使用了synchronized但却不是在多线程环境下运行的,那么编译器与JVM就会判断该锁可以消除,从而避免了多次不必要的加锁与解锁提高了效率。比如线程安全的StringBuffer,其每次append都会涉及加锁与解锁的操作,如果在单线程中那么完全就是在浪费资源。

2.锁粗化

一段逻辑中如果出现多次加锁解锁, 编译器 + JVM 会自动进行锁的粗化。

实际开发过程中, 使用细粒度锁, 是期望释放锁的时候其他线程能使用锁. 但是实际上可能并没有其他线程来抢占这个锁. 这种情况 JVM 就会自动把锁粗化, 避免频繁申请释 放锁。 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值