《Java并发编程的艺术》——synchronized和ReentrantLock

本文的重点放在synchrnozed和ReentrantLock的对比,而不是对它们的介绍

synchronized

synchronized在JDK1.6以前,默认是重量级锁,所以不管资源发生争抢的情况如何,加锁强度都很高(估计这么说吧。。。是在不知道怎么表达),所以会有一些争抢不严重的情况下,性能下降的表现。在JDK1.6以后,引入了锁消除,锁的四种状态等优化机制,使得synchronized得表现没有那么糟糕了(这也是concurrentHashMap在JDK1.8之后取消ReentrantLock使用synchronized的原因)。

ReentrantLock

对锁对控制更加灵活,且可以使用newCondition方法对不同线程进行监视。

相同点

它们都是可重入锁。所谓可重入,指的就是如果线程持有的监视器(加锁本质上就是持有某个资源的监视器)为同一对象时,那么对这一段代码块,就可以不断的重入,举例来说:

synchronized(Person.class{
	synchronized(Person.class){
		...
	}
}
//上述代码块的监视器为同一对象,所以可以再次进入内部的synchronized块,这就叫做重入

重入的本质:

  • 对于同步代码块来说,synchronized其实就是两个指令:monitorenter和monitorexit,进入synchronized块就意味着执行monitorenter指令。对任何资源来说,都有一个monitor与之关联,当对应的monitor被持有后,该资源就被锁定,无法再被其他线程获得。monitor有一个entry count与之关联,默认为0,当monitor被持有后,entry count自增为1,发生重入后,就再自增,… 。退出synchronized块意味着执行monitorexit指令,entry conut自减,什么时候减为0,就意味着该monitor不被持有了,资源释放。
  • 对同步方法来说,则是设置了该方法的ACC_SYNCHRONIZED标志,当线程调用方法时,会检查方法有没有设置这一标志,如果有(对于静态方法,监视器是类的class对象,对非静态方法,监视器是当前类的对象(this) ),则获取相应的monitor,执行上述流程。
  • 注:synchronized里发生异常会直接释放监视器,也就是释放锁
  • 对ReentrantLock来说,重入实际上是不断自增state的值(留在介绍AQS的文章里具体说明)

不同点

  • ReentrantLock可以依靠lockInterruptly方法相应外部的interrupt方法实现可中断锁(线程t1持有资源,线程t2在等待,等太久不想等了,通过在t2中设置lockInterruptly方法响应外部等interrupt实现中断)
  • ReentrantLock通过构造函数参数实现公平锁的设置,默认非公平(synchronized是非公平的)(非公平锁可能会带来吞吐量的提升,但是当线程持有资源时间较长,或请求的平均时长差不多时,带来的提升可能并不明显)
  • ReentrantLock通过tryLock实现超时锁的功能(里面可以设置时间,当线程等待的时候超过这一时间时,就不等了,做其他事情)
  • ReentrantLock通过condition使一个lock对象绑定多个监视器,实现对持有不同监视器对象对线程的通知。

总结

synchronized使用简洁,而ReentrantLock对锁的控制更加细腻,且引入了锁消除,锁的四种状态等优化机制后,synchronized的性能和ReentrantLock也差不多了。在生产环境中,除非要用到ReentrantLock的特有功能,应优先使用synchronized。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值