资讯专栏INFORMATION COLUMN

[JDK源码]J.U.C-AQS-ReentrantLock

不知名网友 / 2072人阅读

摘要:公平锁阻塞队列前边有线程,要去后边排队,简单来说滚后边等着去。非公平锁不管是否有线程排队,先枪锁基于实现的可重入锁实现类。

AQS原理介绍:

AQS (AbstractQueuedSynchronizer)底层一个队列 阻塞队列 ->
​ Abstract:因为它并不知道怎么上锁。模板方法设计模式即可,暴露出锁逻辑。
​ Queue :线程阻塞队列 Synchronizer:同步
​ CAS + state 完成多线程枪锁逻辑 Queue 完成抢不到锁的线程排队

AQS核心代码

//获取锁public final void acquire(int arg) {    if (!tryAcquire(arg) && // 子类判定获取锁是否失败        acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 获取失败后添加到阻塞队列        selfInterrupt();}// 子类实现获取锁的逻辑,AQS并不知道你怎么用这个state来上锁protected boolean tryAcquire(int arg) {    throw new UnsupportedOperationException();}//释放锁的public final boolean release(int arg) {    // 子类判定释放锁成功    if (tryRelease(arg)) {        // 检查阻塞队列唤醒即可        Node h = head;        if (h != null && h.waitStatus != 0)            unparkSuccessor(h);        return true;    }    return false;}// 子类实现获取锁的逻辑protected boolean tryRelease(int arg) {    throw new UnsupportedOperationException();}

子类只需要实现自己的获取锁逻辑和释放锁逻辑即可,至于排队阻塞等待、唤醒机制均由AQS来完成。

公平锁:阻塞队列前边有线程,要去后边排队,简单来说 滚后边等着去。(FIFO) 非公平锁:不管是否有线程排队,先枪锁

ReentrantLock

基于AQS实现的可重入锁实现类。
ReentrantLock 是一个可重入的互斥(/独占)锁,又称为“独占锁”
重入锁: 自己可以再次获取自己的内部的锁。
state 来表示锁状态和重入次数,0无锁,大于0 重入次数,1为重入1次,也即只加锁一次

核心属性

private final Sync sync;//sync,构造方法中初始化,决定使用公平锁还是非公平锁的方式获取锁。

构造方法

	public ReentrantLock() {        // 默认为非公平锁。        sync = new NonfairSync();    }	public ReentrantLock(boolean fair) {        // 由fair变量来表明选择锁类型        sync = fair ? new FairSync() : new NonfairSync();    }

核心内部类

abstract static class Sync extends AbstractQueuedSynchronizer {}
static final class NonfairSync extends Sync {} 非公平锁
static final class FairSync extends Sync {} 公平锁

            //非公平锁        static final class NonfairSync extends Sync {            // 由ReentrantLock调用获取锁            final void lock() {                // 非公平锁,直接抢锁,不管有没有线程排队                if (compareAndSetState(0, 1))                    // 上锁成功,标识当前线程为获取锁的线程                    setExclusiveOwnerThread(Thread.currentThread());                else                    // 抢锁失败,进入AQS的标准获取锁流程                    acquire(1);            }            protected final boolean tryAcquire(int acquires) {                // 使用父类提供的获取非公平锁的方法来获取锁                return nonfairTryAcquire(acquires);            }        }        //公平锁        static final class FairSync extends Sync {            // 由ReentrantLock调用            final void lock() {                // 没有尝试抢锁,直接进入AQS标准获取锁流程                acquire(1);            }            // AQS调用,子类自己实现获取锁的流程            protected final boolean tryAcquire(int acquires) {                final Thread current = Thread.currentThread();                int c = getState();                // 此时有可能正好获取锁的线程释放了锁,也有可能本身就没有线程获取锁                if (c == 0) {                   //这里和非公平锁的区别在于:hasQueuedPredecessors看看队列中是否有线程正在排队,没有的话再通过CAS抢锁                    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;                }                //需要AQS来将当前线程放入阻塞队列,然后进行阻塞操作等待唤醒获取锁                return false;            }        }        abstract static class Sync extends AbstractQueuedSynchronizer {            abstract void lock();            // 非公平锁标准获取锁方法            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()) {                    // 利用state 进行次数记录                    int nextc = c + acquires;                    // 如果超过了int表示范围,表明符号溢出,所以抛出异常                    if (nextc < 0)                        throw new Error("Maximum lock count exceeded");                    setState(nextc);//state 变量赋值                    return true;                }                //表明需要AQS来将当前线程放入阻塞队列,然后进行阻塞操作等待唤醒获取锁                return false;            }            // 公平锁和非公平锁公用方法,在释放锁的时候,并不区分是否公平            protected final boolean tryRelease(int releases) {                int c = getState() - releases;                // 如果当前线程不是上锁的那个线程                if (Thread.currentThread() != getExclusiveOwnerThread())                    throw new IllegalMonitorStateException();                boolean free = false;                // 不是重入锁,那么当前线程一定是释放锁了,把当前AQS用于保存当前锁对象的变量ExclusiveOwnerThread设置为null,表明释放锁成功                if (c == 0) {                    free = true;                    setExclusiveOwnerThread(null);                }                //此时state全局变量没有改变,也就意味着在setState之前                //没有别的线程能够获取锁,保证了以上的操作原子性                setState(c);                //释放锁成功了,可以去唤醒正在等待锁的线程                return free;            }            protected final boolean isHeldExclusively() {                return getExclusiveOwnerThread() == Thread.currentThread();            }            final ConditionObject newCondition() {                return new ConditionObject();            }        }

核心方法

public void lock() {//获取锁的操作    // 直接通过sync同步器上锁    sync.lock();}public void unlock() {//释放锁的操作    sync.release(1);}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/125657.html

相关文章

  • java源码

    摘要:集合源码解析回归基础,集合源码解析系列,持续更新和源码分析与是两个常用的操作字符串的类。这里我们从源码看下不同状态都是怎么处理的。 Java 集合深入理解:ArrayList 回归基础,Java 集合深入理解系列,持续更新~ JVM 源码分析之 System.currentTimeMillis 及 nanoTime 原理详解 JVM 源码分析之 System.currentTimeMi...

    Freeman 评论0 收藏0
  • HashSet源码分析:JDK源码系列

    摘要:简介继续分析源码,上一篇文章把的分析完毕。本文开始分析简单的介绍一下。存储的元素是无序的并且允许使用空的元素。 1.简介 继续分析源码,上一篇文章把HashMap的分析完毕。本文开始分析HashSet简单的介绍一下。 HashSet是一个无重复元素集合,内部使用HashMap实现,所以HashMap的特征耶继承了下来。存储的元素是无序的并且HashSet允许使用空的元素。 HashSe...

    用户83 评论0 收藏0
  • HashMap 源码详细分析(JDK1.8)

    摘要:则使用了拉链式的散列算法,并在中引入了红黑树优化过长的链表。如果大家对红黑树感兴趣,可以阅读我的另一篇文章红黑树详细分析。构造方法构造方法分析的构造方法不多,只有四个。 1.概述 本篇文章我们来聊聊大家日常开发中常用的一个集合类 - HashMap。HashMap 最早出现在 JDK 1.2中,底层基于散列算法实现。HashMap 允许 null 键和 null 值,在计算哈键的哈希值...

    monw3c 评论0 收藏0
  • 集合框架源码学习之HashMap(JDK1.8)

    摘要:所谓拉链法就是将链表和数组相结合。若遇到哈希冲突,则将冲突的值加到链表中即可。在编写程序中,要尽量避免。 目录: 0-1. 简介 0-2. 内部结构分析   0-2-1. JDK18之前   0-2-2. JDK18之后 0-3. LinkedList源码分析   0-3-1. 构造方法   0-3-2. put方法   0-3-3. get方法   0-3-4. resize方法 ...

    yangrd 评论0 收藏0
  • JDK1.8源码分析01之学习建议(可以延伸其他源码学习)

    摘要:唐老师,回答道读源码是要建立在你的基础经验足够的情况下。除了自己去阅读源码之外,比如学习某个类的时候,可以专门结合一些优质的博客针对性的对比学习,并查漏补缺。制定源码学习计划。多调试,跟踪源码。如若有好的学习方法,可以留言一起交流学习。 序言:目前看一看源码,来提升自己的技术实力。同时现在好多面试官都喜欢问源码,问你是否读过JDK源码等等? 针对如何阅读源码,也请教了我的老师。下面就先...

    ky0ncheng 评论0 收藏0

发表评论

0条评论

不知名网友

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<