马桶Java :5.Java AQS 机制 (ReentrantLock)lock()过程

马桶🚽Java 上厕所就能看完的小知识! 欢迎关注、点赞 持续更新!
AQS 机制 (ReentrantLock)

概念

如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程。将共享资源设置为锁定状态。

如果被请求的共享资源被占用就需要一套线程阻塞等待以及被唤醒锁分配的机制,这个机制 AQS 是用 CLH 队列锁实现的

即将暂时获取不到锁的线程加入到队列中。

CLH(Craig,Landin,and Hagersten) 队列是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系 就是类似于 链表节点之间联系的感觉)。AQS 是将每条请求共享资源的线程封装成一个 CLH 锁队列的一个结点(Node)来实现锁的分配。

图中就可以看出公平锁的概念,如果设置为公平锁,那么他就会形成双线链表。按顺序获取资源。等待时间久的排在前面,越早的获取锁。

工作流程 (获取锁 到进入阻塞队列)

来个图

lock.lock();方法开始讲

调用 sync.acquire(1);

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

AbstractQueuedSynchronizer 类中的acquire()

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

首先在ReentrantLock创建时设置公平锁和非公平锁。(默认为非公平锁)

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

然后通过new NonfairSync() 找到了 NonfairSync 其内部继承了Sync 并复写了tryAcquire方法

static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

其次找到 nonfairTryAcquire 方法 这里面涉及到 如何 设置

final boolean nonfairTryAcquire(int acquires) {
     // 获取当前线程
            final Thread current = Thread.currentThread();
     // 获取状态值
            int c = getState();
     // 如果当前状态值为0 及没有线程正在获取资源 我们通过cas操作 来 设置状态为1
            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;
            }
     // 如果状态值不为0 或者过来申请锁的不是当前持有锁的对象 那么返回false
            return false;
        }

如果能获取资源,设置为1 那么就将其设置为排他线程。

然后其他线程进入 就会返回false。继续看方法的后半段 ,因为这里返回为false ! 所以为true,那么会运行短路与的右边

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

首先是添加等待队列

private Node addWaiter(Node mode) {
    // 首先创建一个Node 并将将传入的node 设置为当前线程 THREAD.set(this, Thread.currentThread());
        Node node = new Node(mode);
  
        for (;;) {
             // 获取 尾node节点
            Node oldTail = tail;
            // 如果尾部节点不为空 我们就把当前节点设置为尾部节点 连接上之前的尾部节点
            if (oldTail != null) {
                node.setPrevRelaxed(oldTail);
                // 乐观锁设置 保证 准确
                if (compareAndSetTail(oldTail, node)) {
                    oldTail.next = node;
                    //设置成功 返回给外界
                    return node;
                }
            } else {
                //如果尾节点为空 那么创建一个node 节点设置为头 和 尾
                initializeSyncQueue();
            }
        }
    }

然后看一下cquireQueued

final boolean acquireQueued(final Node node, int arg) {
    // 设置这个中断默认为false
        boolean interrupted = false;
        try {
            for (;;) {
                // 获取当前节点的前一任
                final Node p = node.predecessor();
                // 如果前一任节点为头节点 并且能够获取到锁
                if (p == head && tryAcquire(arg)) {
                    // 将当前节点设置为新的头节点
                    setHead(node);
                    // 将获取到的头节点 的后置节点取消 移除阻塞队列
                    p.next = null; // help GC
                    // 返回中断标识 循环结束
                    return interrupted;
                }
                // 验证前节点是否处于等待状态
                if (shouldParkAfterFailedAcquire(p, node))
                    // 使当前线程进入waiting状态 
                    // 猜测: 按位或的意思等到waiting状态成功返回true
                    interrupted |= parkAndCheckInterrupt();
            }
        } catch (Throwable t) {
            cancelAcquire(node);
            if (interrupted)
                selfInterrupt();
            throw t;
        }
    }

shouldparkAfterFailedAcquire()

 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            // 获取其前节点 是否处于等待状态 如果处于等待状态则放置的位置正确 返回true
            return true;
        if (ws > 0) {
            //如果其前节点放弃锁的竞争 那么就一直去前面找 找到前面没放弃锁竞争的thread 加在他的后面
            
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
           //调用前节点的方法设置前节点的状态为SIGNAL
            pred.compareAndSetWaitStatus(ws, Node.SIGNAL);
        }
        return false;
    }
 private final boolean parkAndCheckInterrupt() {
     // 设置暂停当前节点 等待其他线程唤醒
        LockSupport.park(this);
        return Thread.interrupted();
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,525评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,203评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,862评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,728评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,743评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,590评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,330评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,244评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,693评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,885评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,001评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,723评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,343评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,919评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,042评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,191评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,955评论 2 355

推荐阅读更多精彩内容