天天看点

【Java8源码分析】locks包-ReentrantLock

转载请注明出处: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 == ) {
            // 跟公平锁获取的区别时,这里少了判断队列是否为空的函数hasQueuedPredecessors
            // 即不管排队队列是否为空,该线程都将直接尝试获取锁
            if (compareAndSetState(, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        // 以下这个函数实现了重入锁
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < ) // 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 == ) {
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }
}

//非公平锁的同步器
static final class NonfairSync extends Sync {

    final void lock() {
        if (compareAndSetState(, ))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire();
    }

    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

// 公平锁的同步器
static final class FairSync extends Sync {

    final void lock() {
        acquire();
    }

    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == ) {
            if (!hasQueuedPredecessors() &&
                compareAndSetState(, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < )
                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();
}

// 实现了条件变量
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,状态图如下:

【Java8源码分析】locks包-ReentrantLock

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 = ;
    while (!isOnSyncQueue(node)) {
        // 线程在此挂起
        LockSupport.park(this);
        if ((interruptMode = checkInterruptWhileWaiting(node)) != )
            break;
    }

    // 当收到signal通知后,线程会继续,抢占锁
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        interruptMode = REINTERRUPT;
    if (node.nextWaiter != null) // clean up if cancelled
        unlinkCancelledWaiters();
    // 如果抢占不成功,线程继续挂起
    if (interruptMode != )
        reportInterruptAfterWait(interruptMode);
}

//在等待队列中新建一个Node
private 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)

处被挂起,等待通知,目前的状态图如下:

【Java8源码分析】locks包-ReentrantLock

Step 2

线程C获得锁,执行。当C执行完后,调用

signal

通知

await

的线程

// 将一个等待时间最长的waitQueue的Node移到lockQueue
public 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, ))
        return false;

    Node p = enq(node);
    int ws = p.waitStatus;
    if (ws >  || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
        LockSupport.unpark(node.thread);
    return true;
}
           
【Java8源码分析】locks包-ReentrantLock

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