ReentrantLock锁与内置锁synchronized

Posted 智公博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ReentrantLock锁与内置锁synchronized相关的知识,希望对你有一定的参考价值。

一、内置锁

使用Syschronized 关键字 同步代码块(同步方法)都是使用到对象的内置锁

1、对象内置锁

  1. 使用对象自身的内置锁(监视器锁-monitor lock)
  2. 实例方法-使用实例对象锁,static 方法 使用Class对象锁
  3. 对象内置锁为互斥锁,一个同步块,只有一个线程进入
  4. 同步代码块中的代码具有原子性
  5. 进入代码块内获取到锁,无论正常退出or异常都会释放锁

2、可重入

  1. 可重入,表示内置锁获取锁的粒度是线程,而不是调用
  2. 同一个线程可以重复获取同一个内置锁

3、保护状态

  1. 内置锁可以保证原子性操作
  2. 对象的内置锁和对象本身的状态没有内在关联关系
  3. 很多类使用对象内置锁,单对象的域不一定使用内置锁保护
  4. 一个线程获取到对象的内置锁,其他对象同样还是可以访问该对象,只是获取不了这个对象的锁

java在设计上每个对象都有一个内置锁,只是为了免去需要时显示的创建锁对象

对于包含多个变量的不变形条件,所有变量使用同一个锁来保护,可以保证一致性

4、使用

  1. 尽量缩小同步块的大小,耗时操作如果不是需要同步的,应该在同步块外
  2. 同步代码块如果是耗时的,会带来活跃性或性能问题
  3. 无相关性的同步,可以使用多个、或者拆开到多个同步块中

二、 ReentrantLock 与 synchronized对比

1、相同点

  1. 具有相同的互斥性和内存可见性
  2. 进入同步块与获取ReentrantLock,退出同步块与释放ReentrantLock具有相同内存语义
  3. 同样是可重入

2、区别

  1. 处理锁的不可用性问题更加灵活
  2. 同步块无法中断等待的线程,无法无限等待
  3. 必须(自动)代码块后释放锁,包括异常,无法实现非阻塞结构的加锁规则
  4. ReentrantLocak必须主动释放锁,异常不会自动释放锁,更加危险(忘记了释放)

3、锁的轮询与定时

内置锁中,会出现死锁问题:出现不一致的锁顺序(相互等待),解决的方法只能重启应用

Lock接口中定义的 tryLock()、tryLock(long timeout,TimeUnit unit)方法,可以实现可轮询、可定时的获取锁操作,
在获取不到锁,或超时,可以轮询重试,或者超时退出获取请求,这样可以有效的避免死锁

4、可中断锁

Lock 接口定义的方法 lockInterruptibly()阻塞获取锁,能响应线程中断请求,同步代码块则不能响应中断,只能一直阻塞或者成功获取到锁

5、非块结构加锁

同步代码块的加锁、释放锁都是基于synchronized同步关键字的代码块,自动获取锁、释放锁,使用简单,可以避免忘记释放锁的编程错误;
但这样的加锁规则不灵活,不能自己控制获取和释放

6、锁的公平性

公平锁:

线程按照获取锁的请求顺序获取到锁,一个线程发出获取锁时,如果锁已经由另一线程持有或者有其他线程在队列等待获取锁,那么这个新请求的线程将放入到队列中

非公平锁:

线程获取到锁的顺序与请求锁的顺序不能保证,存在线程直接“插队”获取锁的情况:一个线程发出获取锁时,如果当前锁的状态变为可获取,那么这个新请求锁的线程将直接跳过等待队列并获取到锁

非公平锁比公平锁提供更好的性能:
公平锁在挂起线程和恢复线程时存在的开销降低了性能,在锁竞争激烈的情况下,恢复一个被挂起的线程与该线程真正开始运行存在严重的延迟,举个公平锁例子:A线程持有一个锁,此时B线程请求这个锁,则B被挂起、放入等待队列,当A释放锁时,B将被唤醒,恢复运行再次尝试获取锁;唤醒B并等待恢复运行是有时间消耗的;
假设A释放锁时,线程C也请求这个锁,非公平锁情况下,C可能会在B唤醒前直接获得并使用这个锁,更加充分的使用到了锁的时间,因此吞吐量会更高

默认ReentrantLock与synchronized内置锁都是非公平锁,ReentrantLock也提供了非公平锁的实现,一般情况下,非公平锁时可以符合使用要求,java语言规范没有要求内置锁要实现公平,ReentrantLock也没有降低公平性;

三、synchronized与ReentrantLock选择

ReentrantLock拥有内置锁没有的特性:锁等待(超时,轮询)、可中断的锁等待阻塞、公平性锁、更加灵活可以实现非块结构加锁;
而内置锁的使用更加简单明了,自动获取锁和释放锁,比ReentrantLock更加安全,不会因为忘记释放锁导致不可知问题;
在性能方法,java6后两者实际相差不大。当内置锁不能满足使用需求是,可以考虑使用ReentrantLock,即还是优先使用内置锁

以上只是基于对比说明了内置锁和ReentranLock,没有具体详细的例子,如有不对或不能理解,还请评论交流

以上是关于ReentrantLock锁与内置锁synchronized的主要内容,如果未能解决你的问题,请参考以下文章

锁的总结

锁的总结

Java 多线程之内置锁与显示锁

Java 多线程之内置锁与显示锁

试验局ReentrantLock中非公平锁与公平锁的性能测试

ReentrantLock源码探究探究公平锁与非公平锁背后的奥秘