ReentrantLock

在进行ReentrantLock的源码解析之前先了解些基本概念:

(1)AQS:AbstractQueuedSynchronizer

(2)独占锁:锁在一个时间点只能被一个线程锁占有。根据锁的获取机制,它又划分为公平锁和非公平锁。公平锁是按照通过CLH等待线程按照先来先得的规则公平的获取锁;而非公平锁则当线程要获取锁时会无视CLH等待队列而直接获取锁

(3)共享锁:能被多个线程同时拥有,能被共享的锁

(4)CLH队列:CLH是一个非阻塞的FIFO队列,AQS中管理等待锁的线程队列

(5)CAS:Compare And Swap

 

在高并发的互联网项目中,锁常常用来解决资源共享的问题。ReentrantLock是可重入的互斥锁(可重入锁也叫递归锁,当前线程外层函数获得锁之后内层递归函数仍然可获取该锁),它具有与使用synchronized方法和语句所访问的隐式监视器锁相同的一些基本行为和语义但功能更强大。

1.ReentrantLock的使用

  private final ReentrantLock lock = new ReentrantLock();

   // ...

   public void m() {

     lock.lock();  // block until condition holds

     try {

       // ... method body

     } finally {

       lock.unlock();

     }

   }

 }

2. ReentrantLock的构造函数摘要

ReentrantLock的内部维护了Sync的对象引用,Sync是重入锁ReentrantLock的内部类,它是提供了所有实现机制的同步器。

private final Sync sync;

abstract static class Sync extends AbstractQueuedSynchronizer;

static final class NonfairSync extends Sync;

static final class FairSync extends Sync;

 (1)默认为非公平锁

   public ReentrantLock() {

     sync = new NonfairSync();

}

(2)创建一个具有给定公平策略的ReentrantLock

   public ReentrantLock(boolean fair) {

     sync = fair ? new FairSync() : new NonfairSync();

}

当boolean fair设置为true时,在多个线程的争用下这些锁倾向于将访问权授与等待时间最长的线程,否则此锁将无法保证任何特定访问顺序。使用公平锁的程序在许多线程访问时表现为很低的总体吞吐量,但是在获得锁和保证锁分配的均衡性时差异较小。公平锁不能保证线程调度的公平性,因此使用公平锁的众多线程中的一员可能获得多倍的成功机会,这种情况发生在其他活动线程没有被处理并且目前并未持有锁时。

3.ReentrantLock实现原理解析

  public void unlock() {

    sync.release(1);

}

  public void lock() {

    sync.lock();

  }

ReentrantLock的锁的获取与释放都是由Sync这个类来完成的。Sync是抽象静态内部类,它继承抽象队列同步器:AbstractQueuedSynchronizer。NonfairSync和FairSync获取锁的实现方式是不相同的。

 

NonfairSync锁的获取:

final void lock() {

  if (compareAndSetState(0, 1))

     setExclusiveOwnerThread(Thread.currentThread());

  else

     acquire(1);

}

acquire(1)中1是设置锁的状态的参数,对于独占锁而言,锁处于可获取状态时,它的状态值是0;锁被线程初次获取到了它的状态值就变成了1,如果多次获取则会依次增加数值。

FairSync锁的获取:

final void lock() {

   acquire(1);

}

公平锁和非公平锁获取锁的方式的不同在于:非公平锁会利用CAS去判断state值是否符合预期,如果符合预期则会更新state值,然后把当前线程赋值给exclusiveOwnerThread,如果不符合预期就直接调用acquire(1)获取锁。公平锁和非公平锁的acquire实现方法是相同的,但是其内部tryAcquire实现方法存在差异。

 

获取锁相关源码解析:

* Atomically sets synchronization state to the given updated value if the current * state value equals the expected value.This operation has memory semantics of

* a volatile read and write

protected final boolean compareAndSetState(int expect, int update) {

   return unsafe.compareAndSwapInt(this, stateOffset, expect, update);

}

Note:compareAndSetState是AbstractQueuedSynchronizer的方法,stateOffset是指抽象队列同步器的成员变量state(private volatile int state)的内存偏移地址。由以上源码解析可得知compareAndSwapInt()是sun.misc.Unsafe类中的一个本地方法,它以原子的方式操作当前线程;若当前线程的状态为expect,则设置它的状态为update。

 

* Sets the thread that currently owns exclusive access. A null argument indicates * that no thread owns access.This method does not otherwise impose(利用) any

* synchronization or volatile field accesses.

protected final void setExclusiveOwnerThread(Thread t) {

   exclusiveOwnerThread = t;

}

Note:exclusiveOwnerThread【The current owner of exclusive mode synchronization, private transient Thread exclusiveOwnerThread】是AbstractOwnableSynchronizer的成员变量【AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer】。

 

public final void acquire(int arg) {

  if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))

    selfInterrupt();

}

这里acquire以独占模式获取对象并且忽略中断。通过至少调用一次tryAcquire(int)来实现此方法并在成功时返回。否则在成功之前一直调用tryAcquire(int)将线程加入队列,线程可能重复被阻塞或不被阻塞。可以使用此方法来实现Lock.lock()方法。

Note:tryAcquire试图在独占模式下获取对象状态,此方法应该查询是否允许它在独占模式下获取对象状态,如果允许则获取它。此方法总是由执行acquire的线程来调用。如果此方法报告失败,则acquire方法可以将线程加入队列(如果还没有将它加入队列),直到获得其他某个线程释放了该线程的信号。可以用此方法来实现Lock.tryLock()方法。

实现原理:

1.当前线程首先通过tryAcquire()尝试获取锁,获取成功直接返回;尝试失败则进入到等待队列排序等待
2.当前线程尝试失败,先通过addWaiter(Node.EXCLUSIVE)来将当前线程加入到CLH队列,执行完addWaiter(Node.EXCLUSIVE)之后调用acquireQueued()来获取锁
3.当前线程在执行acquireQueued()时,会进入到CLH队列中休眠等待,直到获取锁了才返回,如果当前线程在休眠等待过程中被中断过,acquireQueued会返回true,此时当前线程会调用selfInterrupt()给自己产生中断

 

FairSync中tryAcquire源码实现:

* Don't grant access unless recursive call or no waiters or is first

protected final boolean tryAcquire(int acquires) {

   final Thread current = Thread.currentThread();

 // 获取同步状态state

   int c = getState();

      if (c == 0) {

        // 队列中不存在等待更长时间的线程(队列中的首个线程)

          // 是首个线程则获取该锁并且设置状态

        if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {

           // 设置为exclusiveOwnerThread

           setExclusiveOwnerThread(current);

           return true;

         }               

// 获取exclusiveOwnerThread并且是当前线程时

      } else if (current == getExclusiveOwnerThread()) {

        int nextc = c + acquires;

        if (nextc < 0)

           throw new Error("Maximum lock count exceeded");

      setState(nextc); // 设置同步状态state

         return true;

      }

      return false;

   }

}

Note:tryAcquire只是尝试获取锁,成功则返回true,失败则返回false,后续再去获取。这里涉及到的state表示锁的状态,对于独占锁state=0表示锁是可获取状态,即锁没有被任何线程锁持有,由于Java中的独占锁是可重入的,故state的值可以大于1。

 

hasQueuedPredecessors源码分析:

* Queries whether any threads have been waiting to acquire longer than the current thread. Note that because cancellations due to interrupts and timeouts may occur at any time, a {@code true} return does not guarantee that some other thread will acquire before the current thread.   

public final boolean hasQueuedPredecessors() {

  // The correctness of this depends on head being initialized

  // before tail and on head.next being accurate if the current

  // thread is first in queue.

  Node t = tail; // Read fields in reverse initialization order

  Node h = head; // Node为AbstractQueuedSynchronizer中的一个内部类

  Node s;

  return h != t && ((s = h.next) == null || s.thread != Thread.currentThread());

}

实现原理:hasQueuedPredecessors通过判断当前线程是不是在CLH队列的队首来返回AQS中是不是有比当前线程等待更久的线程。首先判断state的值,当state为0时,如果队列中不存在等待的线程并且state符合预期值就直接设置为独占线程同时更新state值,返回成功。当state不为0时,并且当前线程是独占线程就直接更新state的值。其它情形下都直接返回false

 

NonfairSync中tryAcquire源码实现:

protected final boolean tryAcquire(int acquires) {

   return nonfairTryAcquire(acquires);

}

*Performs non-fair tryLock.  tryAcquire is implemented in subclasses, but both need nonfair try for trylock method.

final boolean nonfairTryAcquire(int acquires) {

  final Thread current = Thread.currentThread();

  int c = getState(); // 0表示锁没有被任何线程锁拥有

  if (c == 0) {

    if (compareAndSetState(0, acquires)) {

      setExclusiveOwnerThread(current);

      return true;

    }

  }else if (current == getExclusiveOwnerThread()) {

    int nextc = c + acquires;

    if (nextc < 0) // overflow

       throw new Error("Maximum lock count exceeded");

    setState(nextc);

    return true;

  }

  return false;

}

实现原理:首先判断state的值,当state为0时,如果state符合预期值就直接设置为独占线程,同时也会更新state值,返回成功。当state不为0时,并且当前线程是独占线程就直接更新state的值,这里可能存在overflow的情形。其它情形下都直接返回false。

posted @ 2017-09-20 10:18  chen_yong  阅读(352)  评论(0编辑  收藏  举报