ReentrantLock

阅读了ReentrantLock的源码,简单总结了一些实现上的要点如下:

  1. synchronized是Java原生的互斥同步锁,使用方便,对于synchronized修饰的方法或同步块,无需再显式释放锁。synchronized底层是通过monitorentermonitorexit两个字节码指令来实现加锁解锁操作的。
    ReentrantLock做为API层面的互斥锁,需要显式地去加锁解锁。底层是基于AbstractQueuedSynchronizer实现的。AbstractQueuedSynchronizer是JAVA并发很强大的同步器,最终是通过LockSupport提供的park, unpark来实现对线程的阻塞和唤醒的。

  2. 使用isHeldByCurrentThread()或者getHoldCount()判断当前线程是否持有锁。

  3. 支持公平锁和非公平锁。

  • 多线程环境下使用非公平锁时吞吐量会相对比较低,但是可以保证不会饥饿。
  • 锁的公平不代表线程调度的公平性。
    内部实现包含两个内部类:NonfairSyncFairSync
    Sync继承自AbstractQueuedSynchronizer,是个队列。
    默认非公平。
public ReentrantLock() {
     sync = new NonfairSync();
}

3.使用时,记得try{ ... } catch(){} finally { lock.unlock() }

 class X {
   private final ReentrantLock lock = new ReentrantLock();
   // ...

   public void m() {
     lock.lock();  // block until condition holds
     try {
       // ... method body
     } finally {
       lock.unlock()
     }
   }
 }
  1. 调用tryLock()的时候是会忽略公平锁设置的。
public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
}

如果还想保留公平性,调用tryLock(0, TimeUnit.SECONDS)
如果想要一个带时限的公平性又没有那么严格的锁,可以如下:

if (lock.tryLock() || lock.tryLock(timeout, unit)) {
  ...
}
  1. 持有线程对锁拥有状态是有计数的,有多少次lock(),就需要对应多少次的unlock(),一次unlock()只会让计数减1,当计数为0是,当前线程释放锁。

  2. 类似wait(), notify(), notifyAll()与内置锁配合使用,ReentrantLock可以和Condition配合使用,相关的方法有await(), signal(), signalAll(),这些方法均需要在获得锁的情况下调用,否则会报IllegalMonitorStateExceptionawait()的时候会释放锁,当被signal之后,需要重新获得锁,锁计数会恢复。

总结
  ReentrantLock是一种可重入的,可实现公平性的互斥锁,它的设计基于AQS框架,可重入和公平性的实现逻辑都不难理解,每重入一次,state就加1,当然在释放的时候,也得一层一层释放。至于公平性,在尝试获取锁的时候多了一个判断:是否有比自己申请早的线程在同步队列中等待,若有,去等待;若没有,才允许去抢占。

引用
JDK1.8中ReentrantLock源码分析
Java并发系列之ReentrantLock源码分析
第五章 ReentrantLock源码解析1--获得非公平锁与公平锁lock()
Java8源码分析】locks包-ReentrantLock

你可能感兴趣的:(ReentrantLock)