JDK7中ReentrantLock源码解析(1)

来源:互联网 发布:淘宝卖家规则大全2017 编辑:程序博客网 时间:2024/06/07 20:54

在进行ReentrantLock的源码解析(锁的获取与释放)之前先了解些基本概念:

(1)AQS:AbstractQueuedSynchronizer

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

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

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

(5)CAS:Compare And Swap,是原子操作

 

在高并发的互联网项目中,锁常常用来解决资源共享的问题。ReentrantLock是可重入的互斥锁(可重入锁也叫递归锁,当前线程外层函数获得锁之后内层递归函数仍然可获取该锁),它具有与使用synchronized方法和语句所访问的隐式监视器锁相同的一些基本行为和语义但功能更强大。ReentrantLock的序列化与内置锁的行为方式相同:一个反序列化的锁处于解除锁定状态,不管它被序列化时的状态是怎样的。ReentrantLock最多支持同一个线程发起的2147483648个递归锁,试图超过此限制会导致由锁方法抛出的Error。

 

1.ReentrantLock的使用

  private final ReentrantLock lock = new ReentrantLock();

   // ...

   public void m() {

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

     try {

       // ... method body

     } finally {

       lock.unlock();

     }

   }

 }

Note:ReentrantLock提供了一个Condition实现,故通常ReentrantLock都是与Condition共同使用,更多关于Condition可参考:http://ericchunli.iteye.com/blog/2396227,Condition的使用如下:Condition addCondition = lock.newCondition()。

 

 

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实现方法存在差异。

 

获取锁相关源码解析:

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,它具有同volatile读和写相同操作的内存语义。

 

// 设置当前线程的独占访问,null参数表明没有线程拥有过访问,当前方法不会强制执行任何同步或者volatile字段访问

protected final void setExclusiveOwnerThread(Thread t) {

   exclusiveOwnerThread = t;

}

Note:exclusiveOwnerThread【当前同步独占模式的拥有者线程, 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源码实现:

// 不授权访问除非递归调用或者没有等待节点或者是首个节点

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源码分析:

// 查询是否有线程等待的时间比当前线程长,由于中断和超时可能在任何时候发生,true返回并不保证其它线程会在当前线程之前获得锁   

public final boolean hasQueuedPredecessors() {

  // 它的正确性取决于头部的初始化在头和尾之前,接下来如果当前线程是CLH队列中的首个则更加精确

  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);

}

// 执行非公平的tryLock,tryAcquire是在其子类中实现,但都需要对trylock方法进行不公平的尝试

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。

 

实现总结:ReentrantLock的内部维护了Sync的对象引用,Sync是重入锁ReentrantLock的抽象静态内部类,它继承自AbstractQueuedSynchronizer【AQS】。Sync根据ReentrantLock提供的公平锁和非公平锁机制,Sync也提供其对应的实现:FairSync和NonfairSync。Sync提供了所有实现机制的同步器,这里可以看出ReentrantLock锁的获取与释放都是由Sync实现的。

AQS提供了state【大于0表示有线程获得锁,由于重入可以叠加,通过CAS进行内存数值交换】和exclusiveOwnerThread【不为空则表示当前线程获得锁】两个变量来帮助实现ReentrantLock锁的释放与获取。对于非公平锁可以直接利用这两个字段来进行锁定,对于公平锁而言还需要借助AQS中的CLH共同实现。

获取锁的一般步骤是:

1.当前线程首先尝试获取锁,获取成功直接返回;尝试失败则进入到等待队列排序等待
2.当前线程尝试失败,会将当前线程加入到CLH队列

3.接下来当前线程会进入到CLH队列中休眠等待,直到获取锁了才返回【返回中断状态】

4.如果当前线程在休眠等待过程中被中断过,当前线程会调用selfInterrupt()给自己产生中断

 

这里只简单的介绍ReentrantLock是如何获取锁,更多关于如何利用CLH来实现尝试获取锁,可以参考:http://ericchunli.iteye.com/blog/2393222的实现解析。

原创粉丝点击