• 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),使阻塞的线程停止阻塞状态。

    自此,告一段落。

  • 相关阅读:
    bzoj 2038
    ACM训练联盟周赛 A. Teemo's bad day
    The 2018 ACM-ICPC China JiangSu Provincial Programming Contest J. Set
    惊艳,Dubbo域名已改,也不再局限于Java!
    6月份值得一看的 Java 技术干货!
    90 % Java 程序员被误导的一个性能优化策略
    Spring Cloud Finchley 正式发布,包含 4 个重大更新!
    Java 11 快要来了,编译 & 运行一个命令搞定!
    Spring Boot 单元测试详解+实战教程
    Java 10 实战第 1 篇:局部变量类型推断
  • 原文地址:https://www.cnblogs.com/blogofjzq/p/9401453.html
Copyright © 2020-2023  润新知