【Java8源码分析】locks包-ReentrantLock

来源:互联网 发布:咬人猫年龄 知乎 编辑:程序博客网 时间:2024/05/24 06:34

转载请注明出处:http://blog.csdn.net/linxdcn/article/details/72850375


1 辅助内部类

在ReetrantLock内部基于AQS类实现了一个抽象类Sync同步器,对于AQS不熟悉的,可以看看这篇文章:AbstractQueuedSynchronizer同步器。基于Sync还实现了公平锁和非公平锁:

  • 公平锁
    • 线程按照他们发出请求的顺序获取锁
    • 如果有另一个线程持有锁或者有其他线程在等待队列中等待这个所,那么新发出的请求的线程将被放入到队列中。
  • 非公平锁
    • 当一个线程请求非公平锁时,如果在发出请求的同时该锁变成可用状态,那么这个线程可能会跳过队列中所有的等待线程而获得锁
    • 非公平锁上,只有当锁被某个线程持有时,新发出请求的线程才会被放入队列中
//抽象同步器Sync继承AQS,子类可以非公平或者公平锁abstract static class Sync extends AbstractQueuedSynchronizer {    abstract void lock();    // 非公平锁的尝试获取    final boolean nonfairTryAcquire(int acquires) {        final Thread current = Thread.currentThread();        int c = getState();        if (c == 0) {            // 跟公平锁获取的区别时,这里少了判断队列是否为空的函数hasQueuedPredecessors            // 即不管排队队列是否为空,该线程都将直接尝试获取锁            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;    }    protected final boolean tryRelease(int releases) {        int c = getState() - releases;        if (Thread.currentThread() != getExclusiveOwnerThread())            throw new IllegalMonitorStateException();        boolean free = false;        if (c == 0) {            free = true;            setExclusiveOwnerThread(null);        }        setState(c);        return free;    }}//非公平锁的同步器static final class NonfairSync extends Sync {    final void lock() {        if (compareAndSetState(0, 1))            setExclusiveOwnerThread(Thread.currentThread());        else            acquire(1);    }    protected final boolean tryAcquire(int acquires) {        return nonfairTryAcquire(acquires);    }}// 公平锁的同步器static final class FairSync extends Sync {    final void lock() {        acquire(1);    }    protected final boolean tryAcquire(int acquires) {        final Thread current = Thread.currentThread();        int c = getState();        if (c == 0) {            if (!hasQueuedPredecessors() &&                compareAndSetState(0, acquires)) {                setExclusiveOwnerThread(current);                return true;            }        }        else if (current == getExclusiveOwnerThread()) {            int nextc = c + acquires;            if (nextc < 0)                throw new Error("Maximum lock count exceeded");            setState(nextc);            return true;        }        return false;    }}

2 主要方法

ReentrantLock的方法源码比较简单,主要是委托给Sync同步器实现。

//构造函数,默认使用非公平锁同步器public ReentrantLock() {    sync = new NonfairSync();}public ReentrantLock(boolean fair) {    sync = fair ? new FairSync() : new NonfairSync();}public void lock() {    sync.lock();}public void unlock() {    sync.release(1);}// 实现了条件变量public Condition newCondition() {    return sync.newCondition();}

3 条件变量

条件变量是实现了Java中的Condition接口的类,其中Condition中的await、signal、signalAll方法与Object中的wait、notify、notifyAll类似。

假设现在我们有三个线程A、B、C,申请了一个ReetrantLock,以及两个条件变量,如下代码,下面举例说明Condition如何工作

Lock lock = new ReentrantLock();Condition conditionX = lock.newCondition();Condition conditionY = lock.newCondition();

初始状态

假设已经有一个线程获取的了lock,然后线程A、B、C依次获取lock,状态图如下:



Step 1

线程A、B分别调用了conditionX.await(),源码如下

public final void await() throws InterruptedException {    if (Thread.interrupted())        throw new InterruptedException();    // 把等待线程包装为Node    Node node = addConditionWaiter();    // 释放锁    int savedState = fullyRelease(node);    int interruptMode = 0;    while (!isOnSyncQueue(node)) {        // 线程在此挂起        LockSupport.park(this);        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)            break;    }    // 当收到signal通知后,线程会继续,抢占锁    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)        interruptMode = REINTERRUPT;    if (node.nextWaiter != null) // clean up if cancelled        unlinkCancelledWaiters();    // 如果抢占不成功,线程继续挂起    if (interruptMode != 0)        reportInterruptAfterWait(interruptMode);}//在等待队列中新建一个Nodeprivate Node addConditionWaiter() {    Node t = lastWaiter;    if (t != null && t.waitStatus != Node.CONDITION) {        unlinkCancelledWaiters();        t = lastWaiter;    }    Node node = new Node(Thread.currentThread(), Node.CONDITION);    if (t == null)        firstWaiter = node;    else        t.nextWaiter = node;    lastWaiter = node;    return node;}

线程A、B均在函数LockSupport.park(this)处被挂起,等待通知,目前的状态图如下:



Step 2

线程C获得锁,执行。当C执行完后,调用signal通知await的线程

// 将一个等待时间最长的waitQueue的Node移到lockQueuepublic final void signal() {    if (!isHeldExclusively())        throw new IllegalMonitorStateException();    Node first = firstWaiter;    if (first != null)        doSignal(first);}private void doSignal(Node first) {    do {        if ( (firstWaiter = first.nextWaiter) == null)            lastWaiter = null;        first.nextWaiter = null;    } while (!transferForSignal(first) &&             (first = firstWaiter) != null);}final boolean transferForSignal(Node node) {    if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))        return false;    Node p = enq(node);    int ws = p.waitStatus;    if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))        LockSupport.unpark(node.thread);    return true;}




4 总结

(1)synchronized关键字无法中断一个正在等候获得锁的线程,也无法通过轮询得到锁;ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语义,但是添加了类似轮询锁、定时锁等候和可中断锁等候的一些特性。

(2)根类 Object 包含某些特殊的方法,用来在线程的 wait() 、 notify() 和 notifyAll() 之间进行通信;Lock 框架包含了对 wait 和 notify 的概括,这个概括叫作 条件(Condition),对于指定的 Lock ,可以有不止一个条件变量与它关联。

(3)synchronized的优点:使用 synchronized 的时候,不可能忘记释放锁;在退出 synchronized 块时,JVM 会做这件事;当 JVM 用 synchronized 管理锁定请求和释放时,JVM 在生成线程转储时能够包括锁定信息。这些对调试非常有价值,因为它们能标识死锁或者其他异常行为的来源

(4)java.util.concurrent.lock 中的锁定类是用于高级用户和高级情况的工具 。一般来说,除非您对 Lock 的某个高级特性有明确的需要,或者有明确的证据(而不是仅仅是怀疑)表明在特定情况下,同步已经成为可伸缩性的瓶颈,否则还是应当继续使用 synchronized。

参考

https://www.ibm.com/developerworks/cn/java/j-jtp10264/index.html


转载请注明出处:http://blog.csdn.net/linxdcn/article/details/72850375

原创粉丝点击