Java高并发程序设计(五)--ReentrantLock源码解析

说过ReentrantLock的大概操作之后,再来看一下它的源码。

首先说一下几个概念

JUC:java.util.concurrent 包的缩写,计算机方面有时候真的很莫名其妙,总给你弄很多看起来高大上的缩写。

AQS:AbstractQueuedSynchronizer的缩写,翻译过来就是抽象队列同步器,是ReentrantLock的基础。

AQS里面有一个int类型的state,state用volatile修饰来达到它的可见性,用来表示资源的状态,Node类型的head,tail维护一个双向的线程等待队列。

接下来开始看ReentrantLock,

可以看到ReentrantLock有三个内部类,它们的继承关系是,Sync继承刚才所说的AQS,其他两个,一个公平锁,一个非公平锁都继承与Sync。

private final Sync sync;

ReentrantLock的操作,基本就是对sync的操作,通过多态的方式使用公平锁或者非公平锁。

public ReentrantLock() {
        sync = new NonfairSync();
    }
public ReentrantLock(boolean fair) {
        sync = (fair)? new FairSync() : new NonfairSync();
    }

ReentrantLock有两个构造函数,默认构造非公平锁。

接下来看它的最常用方法,lock,unlock。

public void lock() {
        sync.lock();
    }

调用sync的lock,这里我们看非公平锁的。

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

先用AQS里的方法compareAndSetState判断现在的状态,如果现在状态为0,线程直接进入锁,状态改为1。具体实现依赖于Java的unsafe类。

状态为0当然是最理想的状态,这里假设已经有线程占用了,进入else,

进入acquire,acquire是AQS里面的方法

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

 回到不公平锁里的tryAcquire()方法:

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

继续进入nonfairTryAcquire,这个方法在sync里面,

final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            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;
        }

兴许在错过第一次compareAndSetState之后,资源被重新释放,状态归零,所以再判断一次,判断失败进入else if,如果现在占用资源的线程就是现在想要操作的线程,也能获取成功,状态每次加一,线程重入了,所以重入锁叫重入锁。

如果返回true的话,在 if (!tryAcquire(arg) &&这里因为是短路与,方法直接结束,继续假设是其他线程在进行操作,返回false,进入 acquireQueued(addWaiter(Node.EXCLUSIVE), arg))。

private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }

如果在方法中,队列没有更改,线程插到队尾,如果有其他线程也在同样操作导致队列更改,进入enq()方法,总之就是把线程放进队列的合适位置,返回node,继续看acquireQueued,

final boolean acquireQueued(final Node node, int arg) {
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } catch (RuntimeException ex) {
            cancelAcquire(node);
            throw ex;
        }
    }

这里有些不懂,经过查询大概归纳它的意思是,轮流检查状态看能不能获取资源,获取不了就park()阻塞,等待唤醒继续检查。然后循环。

大概流程如下:

图片转载自:http://www.cnblogs.com/waterystone/p/4920797.html

lock完之后看unlock:

public void unlock() {
        sync.release(1);
    }

进入ASQ的release

 public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

回到sync的tryRelease

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

因为可以重入,也要释放重入次数才能使状态重新归零。

当资源彻底被释放之后,ASQ的release里面unparkSuccessor(h),使阻塞的线程停止阻塞状态。

自此,告一段落。

原文地址:https://www.cnblogs.com/blogofjzq/p/9401453.html