Java-并发篇-04-Synchronized和ReentrantLock区别

1. 功能区别

1.1 Synchronized

对于Synchronized来说,它是java语言的关键字,是原生语法层面的互斥,需要jvm实现。
便利性: 很明显Synchronized的使用比较方便简洁,并且由编译器去保证锁的加锁和释放

1.2 ReentrantLock

而ReentrantLock它是JDK 1.5之后提供的API层面的互斥锁,需要lock()和unlock()方法配合try/finally语句块来完成

便利性: 而ReenTrantLock需要手工声明来加锁和释放锁,为了避免忘记手工释放锁造成死锁,所以最好在finally中声明释放锁。

锁的细粒度和灵活度:很明显ReenTrantLock优于Synchronized

2. 性能的区别

在Synchronized优化以前,synchronized的性能是比ReenTrantLock差很多的,但是自从Synchronized引入了偏向锁,轻量级锁(自旋锁)后,两者的性能就差不多了,在两种方法都可用的情况下,官方甚至建议使用synchronized,其实synchronized的优化我感觉就借鉴了ReenTrantLock中的CAS技术。都是试图在用户态就把加锁问题解决,避免进入内核态的线程阻塞。

2.1 Synchronized

Synchronized进过编译,会在同步块的前后分别形成monitorentermonitorexit这个两个字节码指令。在执行monitorenter指令时,首先要尝试获取对象锁。如果这个对象没被锁定,或者当前线程已经拥有了那个对象锁,把锁的计算器加1,相应的,在执行monitorexit指令时会将锁计算器就减1,当计算器为0时,锁就被释放了。如果获取对象锁失败,那当前线程就要阻塞,直到对象锁被另一个线程释放为止。

之所以是重量级,是它因为会去调用OS的mutex函数执行,内核态和用户态切换,内核态是用户不能进入的,如果可以进入岂不是可以搞一堆病毒,哈哈, OS是Operating System的缩写,中文是操作系统的意思

2.2 ReentrantLock

由于ReentrantLock是java.util.concurrent包下提供的一套互斥锁,相比Synchronized,ReentrantLock类提供了一些高级功能,主要有以下3项:

  • 等待可中断
    持锁的线程长期不释放的时候,正在等待的线程可以择放弃等待,这相当于Synchronized来说可以避免出现死锁的情况。通过lock.lockInterruptibly()来实现这个机制。
  • 公平锁
    多个线程等待同一个锁时,必须按照申请锁的时间顺序获得锁,Synchronized锁非公平锁,ReentrantLock默认的构造函数是创建的非公平锁,可以通过参数true设为公平锁,但公平锁表现的性能不是很好。

ReentrantLock lock = new ReentrantLock(true);
查看源码: public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync();

  • 获取锁时限时等待tryLock()
    ReentrantLock还给我们提供了获取锁限时等待的方法tryLock(),可以选择传入时间参数,表示等待指定的时间,无参则表示立即返回锁申请的结果:true表示获取锁成功,false表示获取锁失败。我们可以使用该方法配合失败重试机制来更好的解决死锁问题。

  • 结合Condition实现等待通知机制
    使用synchronized结合Object上的wait和notify方法可以实现线程间的等待通知机制(要么随机唤醒一个线程要么唤醒全部线程)。ReentrantLock结合Condition接口同样可以实现这个功能。而且相比前者使用起来更清晰也更简单更精确,用来实现分组唤醒需要唤醒的线程们。

static Condition notEmpty = lock.newCondition();
static Condition notFull = lock.newCondition();
ReentrantLock lock = new ReentrantLock();
lock.lock(); 加锁
lock.unlock(); 解锁
Condition condition = lock.newCondition(); 获取Condition
condition.await(); 等待
condition.signal(); 唤醒
condition.signalAll(); 唤醒所有
lock.lockInterruptibly(); 中断线程

3. 问题点

3.1 为什么会出现Reentranlock ?

高并发情况下不一定存在竞争,只是局部存在竞争

当线程交替执行(或者单个线程)不存在竞争时:

  • 对Synchronized来说,都必须去调用os.pthread_mutex_lock函数(ps:Synchronized在window下不开源,但是在linux下开源),在内核态和用户态之间切换,内核态是用户进不去的,切换很损耗性能。
  • 对Reentrantlock来说,它继承AQS,交替执行下,队列头和尾始终为0,直接执行,不需要到os层面,在Java层面就可以解决。也就是:单个线程或者交替执行线程它其实和队列无关-jdk级别就解决问题,使用Reentrantlock.lock方法正常返回就是执行方法成功.CAS纳秒执行纳秒级别很快

当线程并发执行时,存在竞争:

  • 对Synchronized来说如上所述
  • 对Reentrantlock来说,他会将线程放到队列当中等候,调用LockSupport.park和unpack方法,这两个方法也是os层面的

private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}

4. 总结:

因为如果使用Synchronized,高并发情况还好,,这才是它重量级锁的原因,而Reentrantlock是java层面的,当然它使用到了CAS,所以才有了reentrantlock,所以在Reentrantlock在线程交替执行下性能远远超过Synchronized ,但是1.6之后Synchronized优化了,两者就差不多了

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alan0517

感谢您的鼓励与支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值