ReentrantLock源码剖析一(概述)

一、简介

众所周知,Java并发组件中的一个重要框架就是AQS,简称同步器。全称AbstractQueuedSynchronizer,它是基于模板方法模式实现的。也就是说大体算法框架已经写好了,具体的一些细节需要用户去实现。AQS中有5个方法是用户根据需要实现的(排它锁与否):

protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
}
protected boolean tryReleaseShared(int arg) {
        throw new UnsupportedOperationException();
}
protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
}
protected int tryAcquireShared(int arg) {
        throw new UnsupportedOperationException();
}
protected boolean isHeldExclusively() {
        throw new UnsupportedOperationException();
}

二、ReentrantLock的骨架

ReentrantLock是排它锁,需要实现上面的两个排它方式的方法。看一下ReentrantLock的内部构造:
ReentrantLock锁依赖于Sync:

public class ReentrantLock implements Lock {
        ...
        private final Sync sync;
        ...
}

并在内部实现了Sync:

abstract static class Sync extends AbstractQueuedSynchronizer {
        abstract void lock();
        final boolean nonfairTryAcquire(int acquires) {
            ...
        }
        protected final boolean tryRelease(int releases){...}
        protected final boolean isHeldExclusively() {...} 
        ...
}

分析:

ReentrantLock 是独占锁,所以说需要实现抽象类 AbstractQueuedSynchronizer 中的两个方法: tryRelease(int releases)tryAcquire(int acquires)
从上面可以看出,它只实现了其中一个 tryRelease,其实 tryAcquire 是在子类中实现了。Sync有两个子类,分别是:

static final class NonfairSync extends Sync {...}
static final class FairSync extends Sync {...}

即非公平锁和公平锁的组件。
1、非公平锁:

static final class NonfairSync extends Sync {
        /**
          * 1)首先基于CAS将state(锁数量)从0设置为1,如果设置成功,设置当前线程为独占锁的线程;-->请求成功-->第一次插队
          * 2)如果设置失败(即当前的锁数量可能已经为1了,即在尝试的过程中,已经被其他线程先一步占有了锁),这个时候当前线程执行acquire(1)方法
          */
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
}

分析:
Sync实现了 tryRealease 方法。在子类 NonfairSync 中,实现了 tryAcquire ,它是以非公平的方式获取锁。上面的acquire()函数在AQS中已经实现了,其中就调用了这里实现的tryAcquire()方法。
2、公平锁:

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();
        //c为0的时候可以获取锁
        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;
    }
}

分析:
在FairSync中,也同样实现了tryAcquire(),它是以公平的方式获取锁。注意和 Sync 中的tryAcquire()作比较。lock()里面的acquire(1)方法在AQS中已经实现了,其中就调用了这里实现的tryAcquire()方法。
好了,至此上面所说的两个函数都实现了: tryReleasetryAcquire

三、具体实现

既然Sync组件已经写好了,那么ReentrantLock中的方法就可以使用它了。上面说到,ReentrantLock依赖组件Sync,即:

public class ReentrantLock implements Lock {
    ...
    private final Sync sync;
    ...
}

下面看一下ReentrantLock的具体实现:
1、构造函数:有两个,默认是非公平的。

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

2、获得锁:

public void lock() {
    sync.lock();
}
public void lockInterruptibly() throws InterruptedException {
    sync.acquireInterruptibly(1);
}
public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}
 public boolean tryLock(long timeout, TimeUnit unit)  throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}   

3、释放锁

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

4、条件变量

public Condition newCondition() {
    return sync.newCondition();
}

5、锁相关的工具性质的方法

public int getHoldCount() {
    return sync.getHoldCount();
}
public boolean isHeldByCurrentThread() {
    return sync.isHeldExclusively();
}
public boolean isLocked() {
    return sync.isLocked();
}
public final boolean isFair() {
    return sync instanceof FairSync;
}
protected Thread getOwner() {
    return sync.getOwner();
}
public final boolean hasQueuedThreads() {
    return sync.hasQueuedThreads();
}
public final boolean hasQueuedThread(Thread thread) {
    return sync.isQueued(thread);
}
public final int getQueueLength() {
    return sync.getQueueLength();
}
protected Collection<Thread> getQueuedThreads() {
    return sync.getQueuedThreads();
}

6、条件变量相关工具类方法

public boolean hasWaiters(Condition condition) {
    if (condition == null)
        throw new NullPointerException();
    if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
    return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
}
public int getWaitQueueLength(Condition condition) {
    if (condition == null)
        throw new NullPointerException();
    if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
        throw new IllegalArgumentException("not owner");
    return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
}
protected Collection<Thread> getWaitingThreads(Condition condition) {
    if (condition == null)
        throw new NullPointerException();
    if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
        throw new IllegalArgumentException("not owner");
    return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);
}

可以看到,ReentrantLock方法的实现全部是依靠Sync的方法。而Sync又是继承了AQS,下面是整个的一个类图。


ReentrantLock类图

这篇文章只是分析个大概,并没有去详细剖析,主要是为了了解ReentrantLock的大致结构,掌握大局。OK。就先到这吧。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容

  • Lock.lock/unlock 接上篇,这篇从Lock.lock/unlock开始。特别说明在没有特殊情况下所有...
    raincoffee阅读 156评论 0 0
  • ReadWriteLock 从这一节开始介绍锁里面的最后一个工具:读写锁(ReadWriteLock)。 Reen...
    raincoffee阅读 630评论 0 1
  • 作者: 一字马胡 转载标志 【2017-11-03】 更新日志 前言 在java中,锁是实现并发的关键组件,多个...
    一字马胡阅读 44,128评论 1 32
  • ReentranLock从字面上理解就是可重入锁,它支持同一个线程对资源的重复加锁,也是我们平时在处理java并发...
    Justlearn阅读 1,611评论 1 5
  • ReentrantLock和synchronized一样加锁方式,独占的获取同步的对象锁。 1. 简介 R...
    Skymiles阅读 1,194评论 0 1