第十三章 ReentrantLock 简介

  Java 5.0 提供的新的加锁机制:当内置加锁机制不适合时 , 作为一种可选择的高级功能

  一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。

13.1 Lock 与 ReentrantLock

  Lock 中实现的必须提供与内置锁相同的内存可见

性语义, 但在加锁语义,调度算法,顺序保证和性能特性方面有所不同.

13.1.1 轮询锁和定时锁

  在内置锁中, 解决死锁的唯一方法是 重新启动程序. 防止死锁的唯一方法是 构造程序时避免出现不一致的锁顺序(互相等待).

  避免死锁的方法 :

    通过定时或轮询的方式去获取锁 : 如果不能获得所有需要的锁, 则释放所有已经获得的锁. 然后重新尝试获取所有锁.

  内置锁在请求锁之后 ,此操作无法取消.

13.1.2 可中断的锁获取操作

  不可中断的阻塞机制使得任务的取消变得复杂. 

  tryLock() 方法可实现定时的和可中断的所获取操作. 

13.1.3 非块结构的加锁

  内置锁中,  进入synchronize 代码块自动加锁, 执行完代码块自动释放锁. 锁的获取与释放在同一代码块中. 简化了程序的分析, 但缺少一定的灵活性.

  显示锁中, 加锁操作和释放锁操作可以不在同一个代码块中 , 提高的锁的灵活性 . 但是也带来一定的危险性(比如忘了释放锁)

  通过降低锁的粒度可以提高代码的可伸缩性.

  锁分段技术: 在基于散列的容器中实现不同的散列链, 以便使用不同的锁. 

  可采用类似的原则降低链表中锁的粒度 .

  连锁式加锁或者锁耦合 : 对链表的每个节点加锁, 操作此节点时必须拥有此节点的锁, 只有获取到下个节点的锁时才释放上个节点的锁.

13.2 锁的性能

  Java 5.0 中, 多线程环境下 显示锁的性能优于内置锁.

  Java 6.0 中, 内置锁使用类似与显示锁的算法进行优化, 两者的性能 基本接近.

13.3 公平性

  ReentrantLock 在构造时可选择使用公平锁和非公平锁.

  • 公平锁中, 若有线程持有锁或有其他线程在队列中等待这个锁, 则新发出请求这个锁的线程将被放入队列中.
  • 非公平锁中, 只有当锁被某个线程持有时, 新发出请求锁的线程才会被放入队列中.
  • 公平锁由于线程的挂起和恢复的开销(线程从挂起状态到恢复运行状态需要一定的时间)而极大的降低性能..
  • 非公平时可能在某个线程在其它线程争抢锁之前就已经锁的使用,这样并不影响其它线程的使用,提高了性能

  选择 : 当持有锁的时间相对较长或者请求锁的平均时间间隔较长时可选择使用 公平锁

13.4 synchronize 和 ReetrantLock

  • 仅当内置锁不满足需求时才考虑使用显示锁.
  • 内置锁在线程转储中能给出在哪些调用帧中获得了哪些锁 , 并能够检测和识别发生死锁的线程. 而显示锁不可以
  • synchronize 是JVM的内置属性, 优化方面更加方便. 推荐使用内置锁.

13.5 读---写锁(ReadWriteLock)

  • 一个资源科被多个读操作访问, 或者被一个写操作访问. 但两者不能同时进行.
  • 非公平模式 : 连续竞争的非公平锁可能无限期地推迟一个或多个 reader 或 writer 线程,但吞吐量通常要高于公平锁。
  • 公平模式 : 当释放当前保持的锁时,可以为等待时间最长的单个 writer 线程分配写入锁,如果有一组等待时间大于所有正在等待的 writer 线程 的 reader 线程,将为该组分配写入锁(防止公平模式下写线程饥饿)
  • 重入还允许从写入锁降级为读取锁,其实现方式是:先获取写入锁,然后获取读取锁,最后释放写入锁。但是,从读取锁升级到写入锁是不可能的
原文地址:https://www.cnblogs.com/virgosnail/p/9446523.html