文档章节

ReentrantLock源码学习

z
 zengjiaqwe
发布于 2017/09/03 18:15
字数 1015
阅读 0
收藏 0

ReentrantLock实际是通过AQS同步器来实现的,AQS本质是一个FIFO队列,利用volatile修饰的status维护状态,基于先进先出的队列管理多线程请求。通过Unsafe类compareAndSwap方法维护status状态的修改,这是一个natvie方法,使用硬件层面的并发;

ReentrantLock构造方法

默认是非公平锁

public ReentrantLock() {
    sync = new NonfairSync();
}

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

一、非公平锁

1.NonfairSync类 lock()

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

利用CAS将同步器状态从0改为1成功,则将当前排他锁的持有者设置为当前线程;

否则,再来看acquire()方法,acquire是父类AQS的方法

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

    先尝试获取锁,调用tryAcquire(),NonfairSync的tryAcquire()实际调用nonfairTryAcquire()

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

    如果同步器状态为0,利用CAS修改同步器状态,修改成功,设置同步器的持有线程为当前线程;如果同步器持有的线程已经是当前线程,则将同步器的状态值累加。(一个线程可以多次lock);

    如果尝试获取锁失败,回到acquire方法;先构造一个Node,看AQS中的addWaiter方法实现

    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)) {
                // 队列已经初始化,在尾部增加node结点
                pred.next = node;
                return node;
            }
        }
        // 自旋,队列未初始化或由于其它线程竞争导致队列尾部添加结点失败时执行enq(),把node结点放入尾部
        enq(node);
        return node;
    }

    第一个构建Node的线程,会先进入到enq方法,enq方法具体实现如下:

    private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

        初始化流程,创建一个新Node节点t,并且设置为头部节点

        此时head和tail都指向了新构造的t这个Node

        再将node节点,设置到队列尾部

        

    acquireQueued()

    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            // 自旋尝试获取锁
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    // 发现node的前驱是头结点并且node获取状态成功,则释放头结点
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                // 进入阻塞
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

PS:如果当前线程节点的前驱节点不是头部节点,会进入阻塞状态,进入阻塞时会将前驱节点状态设置为signal,当前驱节点对应的线程释放锁时,会根据signal唤醒下一个节点对应的线程。阻塞是利用Unsafe中的park(),native方法。

 

 

 

 

 

2.AQS的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;
    }

 

二、公平锁

// FairSync lock 公平锁lock
final void lock() {
    acquire(1); // AQS类方法
}

// AQS类方法
public final void acquire(int arg) {
    // 对比非公平锁,没有走快捷方法直接拿到锁,会先判定等待队列中是否有其它线程节点,有的话会直接进入等待队列
    // 会比非公平锁有更多的线程上下文切换,高并发场景下,性能不如非公平锁
    if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

// FairSync tryAcquire
protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 确认当前没有线程拿到锁,判断等待队列中是否有线程节点等待,如果没有,当前线程尝试更改AQS状态
        if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    } else if (current == getExclusiveOwnerThread()) {
        // 同一个线程多次lock操作
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

 

© 著作权归作者所有

共有 人打赏支持
z
粉丝 0
博文 1
码字总数 1015
作品 0
杭州
111 多线程JUC包下代码分析

Java多线程系列目录(共43篇) AtomicLongFieldUpdater:通过反射+CAS实现对传入对象的指定long字段实现类似AtomicLong的操作 http://www.cnblogs.com/skywang12345/p/javathreadscategory.ht...

素雷
2017/10/31
0
0
再一次理解ReentrantLock

1. ReentrantLock的介绍 ReentrantLock重入锁,是实现Lock接口的一个类,也是在实际编程中使用频率很高的一个锁,支持重入性,表示能够对共享资源能够重复加锁,即当前线程获取该锁再次获取不...

你听___
2017/11/07
0
0
Java锁之ReentrantLock(一)

一、ReenTrantLock结构 图1-1 根据上图可以知道,ReenTrantLock继承了Lock接口,Lock接口声明方法如下: 方法名 说明 抛出异常 lock() 一直阻塞获取锁,直到获取成功 无 lockInterruptibl...

木木匠
08/10
0
0
Concurrent包学习(一)

java.util.concurrent包下面提供了很多多并发编程的工具和框架,locks 下面提供了锁相关的工具,例如ReentrantLock(可重入锁)、condition等在其他的类中经常有使用,提供了HashMap、Queue...

alvaDing
2016/09/24
27
0
ArrayBlockingQueue 源码学习

ArrayBlockingQueue public E take() throws InterruptedException { // take an element from the queue , will block if the queue is empty final ReentrantLock lock = this.lock; lock.......

Yixin_Nemo
08/08
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

[雪峰磁针石博客]软件测试专家工具包1web测试

web测试 本章主要涉及功能测试、自动化测试(参考: 软件自动化测试初学者忠告) 、接口测试(参考:10分钟学会API测试)、跨浏览器测试、可访问性测试和可用性测试的测试工具列表。 安全测试工具...

python测试开发人工智能安全
今天
2
0
JS:异步 - 面试惨案

为什么会写这篇文章,很明显不符合我的性格的东西,原因是前段时间参与了一个面试,对于很多程序员来说,面试时候多么的鸦雀无声,事后心里就有多么的千军万马。去掉最开始毕业干了一年的Jav...

xmqywx
今天
2
0
Win10 64位系统,PHP 扩展 curl插件

执行:1. 拷贝php安装目录下,libeay32.dll、ssleay32.dll 、 libssh2.dll 到 C:\windows\system32 目录。2. 拷贝php/ext目录下, php_curl.dll 到 C:\windows\system32 目录; 3. p...

放飞E梦想O
今天
0
0
谈谈神秘的ES6——(五)解构赋值【对象篇】

上一节课我们了解了有关数组的解构赋值相关内容,这节课,我们接着,来讲讲对象的解构赋值。 解构不仅可以用于数组,还可以用于对象。 let { foo, bar } = { foo: "aaa", bar: "bbb" };fo...

JandenMa
今天
1
0
OSChina 周一乱弹 —— 有人要给本汪介绍妹子啦

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @莱布妮子 :分享水木年华的单曲《中学时代》@小小编辑 手机党少年们想听歌,请使劲儿戳(这里) @须臾时光:夏天还在做最后的挣扎,但是晚上...

小小编辑
今天
68
8

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部