Java并发编程(七):深入浅出队列同步器AQS原理及面试要点

JAVA面试题分享五百四十九:JUC中的AQS底层详细超详解,剖析AQS设计中所需要考虑的各种问题 / cas方法如果state上限只有1,那么就是独占模式Exclusive,例如 ReentrantLock如果state上限大于1,那就是共享模式Share,例如 Semaphore、CountDownLatch、ReadWriteLock,CyclicBarrier对外暴露的getter/setter方法,是走不了CAS的。而且setter/getter没有被synchronized修饰。所以必须要volatile,保证可见性。 阅读详情


在这里插入图片描述

1 AQS简介

AQS,即AbstractQueuedSynchronizer,在同步组件的实现中,AQS是核心部分,同步组件的实现者通过使用AQS提供的模板方法实现同步组件语义,AQS则实现了对同步状态的管理,以及对阻塞线程进行排队,等待通知等等一些底层的实现处理。

2 同步队列CLH

CLH(Craig, Landin, and Hagersten locks)同步队列是AQS对同步状态的管理的基石。 同步队列是一个FIFO双向队列,其内部通过头尾指针head和tail记录队首和队尾元素,队列元素的类型为Node。AQS依赖它来完成同步状态state的管理,当前线程如果获取同步状态失败时,AQS则会将当前线程已经等待状态等信息构造成一个节点(Node)并将其加入到CLH同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点唤醒(公平锁),使其再次尝试获取同步状态。

2.1 同步状态state的维护

state是在AQS中维护的唯一的共享状态,用来实现同步器同步。相关源码如下所示:

/**
 * The synchronization state.
 */
private volatile int state;

/**
 * Returns the current value of synchronization state.
 * This operation has memory semantics of a {@code volatile} read.
 * @return current state value
 */
protected final int getState() {
    return state;
}

/**
 * Sets the value of synchronization state.
 * This operation has memory semantics of a {@code volatile} write.
 * @param newState the new state value
 */
protected final void setState(int newState) {
    state = newState;
}

/**
 * Atomically sets synchronization state to the given updated
 * value if the current state value equals the expected value.
 * This operation has memory semantics of a {@code volatile} read
 * and write.
 *
 * @param expect the expected value
 * @param update the new value
 * @return {@code true} if successful. False return indicates that the actual
 *         value was not equal to the expected value.
 */
protected final boolean compareAndSetState(int expect, int update) {
    // See below for intrinsics setup to support this
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

state设计要点:

  1. state用volatile修饰,保证多线程中的可见性。
  2. getState()和setState()方法采用final修饰,限制AQS的子类重写它们。
  3. compareAndSetState()方法采用乐观锁思想的CAS算法,也是采用final修饰的,不允许子类重写。
2.2 Node节点

CLH同步队列中,一个节点表示一个线程,它保存着线程的引用(thread)、状态(waitStatus)、前驱节点(prev)、后继节点(next),condition队列的后续节点(nextWaiter)如下图:
在这里插入图片描述
waitStatus几种状态状态:
在这里插入图片描述

2.3 AQS原理图

我们结合state和Node节点知识给出AQS的原理图:
在这里插入图片描述

2.4 入队

既然说到了队列,那么肯定有入队和出队操作,AQS是如何实现的呢?先看源码:

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

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

首先是调用addWaiter()方法,通过CAS操作将新节点插入到当前队列末尾,要是插入成功则返回,否则调用enq()方法:先判断队尾元素是否存在,如果不存在则创建一个结点并让头尾指针同时指向该节点,之后再对新的节点执行插入操作。

注意这里的入队操作都是尾插法。

2.5 出队

CLH队列中首节点的线程释放同步状态后,将会唤醒它的后继节点(next),而后继节点将会在获取同步状态成功时将自己设置为首节点。可以看一下以下源码:

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

private void unparkSuccessor(Node node) {
    /*
     * If status is negative (i.e., possibly needing signal) try
     * to clear in anticipation of signalling.  It is OK if this
     * fails or if status is changed by waiting thread.
     */
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);

    /*
     * Thread to unpark is held in successor, which is normally
     * just the next node.  But if cancelled or apparently null,
     * traverse backwards from tail to find the actual
     * non-cancelled successor.
     */
    Node s = node.next;
    // 队首元素的下一节点为null或下一节点的等待状态为已取消
    // 此时从队尾元素向前遍历,找到距队首元素最近且状态为未取消的节点
    if (s == null || s.waitStatus > 0) {
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    // 此处执行唤醒操作
    if (s != null)
        LockSupport.unpark(s.thread);
}

3 条件队列Condition

我们都知道,synchronized控制同步的时候,可以配合Object的wait()、notify(),notifyAll() 系列方法可以实现等待/通知模式。而Lock呢?它提供了条件Condition接口,配合await(),signal(),signalAll() 等方法也可以实现等待/通知机制。ConditionObject实现了Condition接口,给AQS提供条件变量的支持 。我们先来看下图:
在这里插入图片描述

ConditionObject队列与CLH队列的爱恨情仇:

  • 调用了await()方法的线程,会被加入到conditionObject等待队列中,并且唤醒CLH队列中head节点的下一个节点。
  • 线程在某个ConditionObject对象上调用了singnal()方法后,等待队列中的firstWaiter会被加入到AQS的CLH队列中,等待被唤醒。
  • 当线程调用unLock()方法释放锁时,CLH队列中的head节点的下一个节点(在本例中是firstWaiter),会被唤醒。

ConditionObject对象维护了一个单独的等待队列,AQS所维护的CLH队列是同步队列,它们节点类型相同,都是Node。

4 独占式锁

// 独占式获取同步状态,如果获取失败则插入同步队列进行等待
void acquire(int arg);
// 与acquire方法相同,但在同步队列中进行等待的时候可以检测中断
void acquireInterruptibly(int arg);
// 在acquireInterruptibly基础上增加了超时等待功能,在超时时间内没有获得同步状态返回false
boolean tryAcquireNanos(int arg, long nanosTimeout);
// 释放同步状态,该方法会唤醒在同步队列中的下一个节点
boolean release(int arg);
4.1 独占锁的获取
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

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)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

acquire()获得同步状态成功与否做了两件事情:1. 成功,则方法结束返回(逻辑与运算符&&的短路特性),2. 失败,则先调用addWaiter()入队,返回最新的队列作为调用acquireQueued()方法的参数。acquireQueued()判断当前节点的先驱元素是不是队首元素,如果是并且尝试获取锁成功则该节点获取锁成功并执行出队操作。注意p.next=null这句,并发的时候可能会有很多节点,如果节点获取到锁之后为了释放JVM内存需要将对象引用手动指向null。关于这点少侠之后会单独写一篇,如果不这么做,并发情况下可能会导致FULL GC发生。
独占锁的获取流程如下(图片来源网络,侵删):
在这里插入图片描述

4.2 独占锁的释放
public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
    
private void unparkSuccessor(Node node) {
        /*
         * If status is negative (i.e., possibly needing signal) try
         * to clear in anticipation of signalling.  It is OK if this
         * fails or if status is changed by waiting thread.
         */
        int ws = node.waitStatus;
        if (ws < 0)
            node.compareAndSetWaitStatus(ws, 0);

        /*
         * Thread to unpark is held in successor, which is normally
         * just the next node.  But if cancelled or apparently null,
         * traverse backwards from tail to find the actual
         * non-cancelled successor.
         */
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node p = tail; p != node && p != null; p = p.prev)
                if (p.waitStatus <= 0)
                    s = p;
        }
        if (s != null)
            LockSupport.unpark(s.thread);
    }

这段代码逻辑就比较容易理解了,如果同步状态释放成功(tryRelease返回true)则会执行if块中的代码,当head指向的头结点不为null,并且该节点的状态值不为0的话才会执行unparkSuccessor()方法。
通过源码的学习,现在我们知道:

  • 线程获取锁失败,线程被封装成Node进行入队操作,核心方法在于addWaiter()和enq(),同时enq()完成对同步队列的头结点初始化工作以及CAS操作失败的重试;
  • 线程获取锁是一个自旋的过程,当且仅当 当前节点的前驱节点是头结点并且成功获得同步状态时,节点出队即该节点引用的线程获得锁,否则,当不满足条件时就会调用LookSupport.park()方法使得线程阻塞;
  • 释放锁的时候会唤醒后继节点;

总体来说:在获取同步状态时,AQS维护一个同步队列,获取同步状态失败的线程会加入到队列中进行自旋;移除队列(或停止自旋)的条件是前驱节点是头结点并且成功获得了同步状态。在释放同步状态时,同步器会调用unparkSuccessor()方法唤醒后继节点。

4.3 可中断式获取锁(acquireInterruptibly方法)

lock相较于synchronized有一些更方便的特性,比如能响应中断以及超时等待等特性,现在我们依旧采用通过学习源码的方式来看看能够响应中断是怎么实现的。

public final void acquireInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (!tryAcquire(arg))
            doAcquireInterruptibly(arg);
    }
    
    private void doAcquireInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.EXCLUSIVE);
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    return;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } catch (Throwable t) {
            cancelAcquire(node);
            throw t;
        }
    }

现在看这段代码就很轻松了吧,与acquire方法逻辑几乎一致,唯一的区别是当parkAndCheckInterrupt返回true时,即线程阻塞时该线程被中断,代码抛出被中断异常。

4.4 超时等待式获取锁(tryAcquireNanos()方法)

通过调用lock.tryLock(timeout,TimeUnit)方式达到超时等待获取锁的效果,该方法会在三种情况下才会返回:

  • 在超时时间内,当前线程成功获取了锁;
  • 当前线程在超时时间内被中断;
  • 超时时间结束,仍未获得锁返回false。

附上tryAcquireNanos()方法的源码实现:

public final boolean tryAcquireNanos(int arg, long nanosTimeout)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    return tryAcquire(arg) ||
		//实现超时等待的效果
        doAcquireNanos(arg, nanosTimeout);
}

private boolean doAcquireNanos(int arg, long nanosTimeout)
        throws InterruptedException {
    if (nanosTimeout <= 0L)
        return false;
    final long deadline = System.nanoTime() + nanosTimeout;
    final Node node = addWaiter(Node.EXCLUSIVE);
    boolean failed = true;
    try {
        for (;;) {
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return true;
            }
            nanosTimeout = deadline - System.nanoTime();
            if (nanosTimeout <= 0L)
                return false;
            if (shouldParkAfterFailedAcquire(p, node) &&
                nanosTimeout > spinForTimeoutThreshold)
                LockSupport.parkNanos(this, nanosTimeout);
            if (Thread.interrupted())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

代码逻辑图如下:
在这里插入图片描述

5 共享式锁

// 共享式获取同步状态,与独占式的区别在于同一时刻有多个线程获取同步状态;
void acquireShared(int arg);
// 在acquireShared方法基础上增加了能响应中断的功能;
void acquireSharedInterruptibly(int arg);
// 在acquireSharedInterruptibly基础上增加了超时等待的功能;
boolean tryAcquireSharedNanos(int arg, long nanosTimeout);
// 共享式释放同步状态
boolean releaseShared(int arg);
5.1 共享锁的获取(acquireShared())

首先来看下实现源码:

public final void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)
        doAcquireShared(arg);
}

private void doAcquireShared(int arg) {
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            if (p == head) {
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    if (interrupted)
                        selfInterrupt();
                    failed = false;
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

这段源码的逻辑很容易理解,在该方法中会首先调用tryAcquireShared方法,tryAcquireShared返回值是一个int类型,当返回值为大于等于0的时候方法结束说明获得成功获取锁,否则,表明获取同步状态失败即所引用的线程获取锁失败,会执行doAcquireShared()方法,注意该方法里的自旋过程中能够退出的条件是当前节点的前驱节点是头结点并且tryAcquireShared(arg)返回值大于等于0即能成功获得同步状态。

5.2 共享锁的释放(releaseShared())
public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}

private void doReleaseShared() {
    /*
     * Ensure that a release propagates, even if there are other
     * in-progress acquires/releases.  This proceeds in the usual
     * way of trying to unparkSuccessor of head if it needs
     * signal. But if it does not, status is set to PROPAGATE to
     * ensure that upon release, propagation continues.
     * Additionally, we must loop in case a new node is added
     * while we are doing this. Also, unlike other uses of
     * unparkSuccessor, we need to know if CAS to reset status
     * fails, if so rechecking.
     */
    for (;;) {
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;            // loop to recheck cases
                unparkSuccessor(h);
            }
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;                // loop on failed CAS
        }
        if (h == head)                   // loop if head changed
            break;
    }
}

这段代码跟独占式锁释放过程有点点不同,在共享式锁的释放过程中,对于能够支持多个线程同时访问的并发组件,必须保证多个线程能够安全的释放同步状态,这里采用的CAS保证,当CAS操作失败continue,在下一次循环中进行重试。

5.3 可中断(acquireSharedInterruptibly()方法),超时等待(tryAcquireSharedNanos()方法)

关于可中断锁以及超时等待的特性其实现和独占式锁可中断获取锁以及超时等待的实现几乎一致,少侠建议读者们结合源码自己深入的理解一下。

6 小结

关于AQS的面试点主要包含以下几点:

  1. state 状态的维护。
  2. CLH队列
  3. ConditionObject通知
  4. 模板方法设计模式
  5. 独占与共享模式。
  6. 自定义同步器。
  7. AQS全家桶的一些延伸,如:ReentrantLock等。

点点关注,不会迷路

淘宝面试常问:AQS抽象队列同步器原理? AQS,Abstract Queued Synchronizer,抽象。 阅读详情

相关推荐

Java 并发编程:ReentrantLock 锁与 AQS

大家好,我是栗筝i,这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 030 篇文章,在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验,并希望进一步完善自己对整个 Java 技术体系来充实自己的技术栈的同学。与此同时,本专栏的所有文章,也都会准备充足的代码示例和完善的知识点梳理,因此也十分适合零基础的小白和要准备工作面试的同学学习。

栗筝i的博客 2816

Java并发编程的艺术之独占式同步状态的获取与释放源码分析

文章目录1 问题背景2 源码分析2.1 同步状态的获取2.1.1 acquire()方法2.1.2 addWaiter()以及enq()方法2.1.3 acquireQueued()方法2.2 同步状态的释放 1 问题背景 前面了解了Java并发编程之同步队列,今天研究独占式同步状态的获取与释放的源码分析。 本博客来自《Java并发编程的艺术》的读后感 2 源码分析 2.1 同步状态的获取 2.1.1 acquire()方法 同步状态的获取主要由模板方法acquire(int arg)实现。该方法

Android_la的博客 325

文件系统同步器

文件系统同步器,其中包含了三个解决方案,从简单到不断的重构完成,值得学习,其中最后一个解决方案使用.net4.7

使用C#同步器将更新业务用户的基本信息编写.NET分析器高级篇

同步器将更新业务用户的基本信息

kjlkkk96563215的博客 114

全新RGB-D及平面法线数据集DIODE

丰田工业大学联合芝加哥大学和北航发布全新RGB-D及平面法线数据集 DIODE (Dense Indoor and Outdoor DEpth)。

qq_43477338的博客 1540

能量机关 STM32HAL库开发大疆A板WS2812B彩灯(PWM+DMA)

本文主要目的是为了制作RoboMaster比赛能量机关的灯条控制,模拟比赛场中大符,搭建出类似场地道具,以供视觉测试 使用的是RoboMaster开发板A型,芯片为STM32F427IIH6,使用的灯条为WS2812B。 采用PWM+DMA控制,使用CUBEMX配置底层,keil5编辑烧录代码 实现:一共五片扇叶,每片扇叶均实现流水灯+常亮颜色 参考文章在后面会标记出来

m0_51756449的博客 4291

Semaphore&CyclicBarrier源码分析

1 如何保证原子操作 int remaining = available - acquires; if (remaining < 0 || compareAndSetState(available, remaining)) return remaining; 2 共享锁和独占锁的区别: 共享锁:一个线程获取锁成功后,出队的时候就去唤醒下一个线程。 独占锁:线程释放锁时才会...

1 214

3-RRR并联机器人结构详解:从运动学到奇异点分析(附MATLAB仿真代码)

本文详细解析了3-RRR并联机器人的结构设计、运动学建模及奇异点分析,并提供了MATLAB仿真代码。通过对称性设计和可分析性,3-RRR结构成为教学与研究的理想对象。文章从基础杆件参数设置到奇异位形的实时捕捉,深入探讨了机构运动的数学本质,并分享了仿真调试中的实用技巧,助力工业自动化和精密控制应用。

sunny的博客 366

抽象队列式同步器AQS详解

AQS源码详解 AQS,抽象的队列式的同步器,AQS定义了一套多线程访问共享资源的同步器框架,ReetrantLock、Semaphore、CountDownLatch的实现均依赖于AQS,所以详细了解其源码实现,有助于理解依赖其实现的 锁和同步器。 AQS维护一个双向的FIFO阻塞队列和一个volatile int类型的成员变量,如图所示,其实简而言之,多个线程同时对资源进行抢占时,失败的线程会...

zycxnanwang的博客 1641

LAMMPS实操系列(一): 大量FCC-CoCrCuFeNi高熵合金建模与最稳定结构筛选

高熵合金(High-entropy alloys,HEA),是由五种或五种以上等量或大约等量金属形成的合金。由于高熵合金可能具有许多优秀的物理化学性质,因此在材料科学领域受到广泛关注

Ceshigo_CD的博客 3257

Comsol 超声空化气泡仿真

当液体中受到高能量的超声波作用时,液体中的压力会发生剧烈变化,形成局部的低压区域。当液体中的压力降低到饱和蒸汽压以下时,就会发生空化现象,即液体中形成微小气泡。这种空化常用于一些工业应用,如清洗、乳化、混合等。这种空化常用于一些医学和生物学应用,如碎石术、肿瘤治疗、细胞破碎等。超声空化的应用非常广泛。在医学领域,超声空化广泛应用于诊断和治疗,如超声造影、超声刀、超声碎石术等。在工业领域,超声空化可用于清洗、乳化、混合等工艺。此外,超声空化还在食品加工、化学合成、环境保护等领域有着重要应用。

声学黑洞仿真工作室 2556

【Java并发编程】AQS(2)——独占锁的获取

今天是4月4日,清明节第一天,互联网一片灰白,大家都在缅怀逝者,致敬英烈。所以今天我也没有过多的娱乐,一天都在鼓捣这篇文章。今天这篇主要说说AQS独占锁的获取。 AQS中对独占锁的获取一共有三个方法,今天主要说第一个 acquire:不响应中断获取独占锁 acquireInterruptibly:响应中断获取独占锁 tryAcquireNanos:响应中断+超时获...

哈喽羊 1679

第3版•系统集成项目管理工程师(中级) │ 第6章 数据工程

🥏第3版•系统集成项目管理工程师(中级) │ 第6章 数据工程

✨ 欢迎来到【Seal ^_^ 的优快云博客】!✨ 1万+

AQS(AbstractQueuedSynchronizer,队列同步器)源码解读

AQS 的全称为 AbstractQueuedSynchronizer ,翻译过来的意思就是抽象队列同步器。这个类在 java.util.concurrent.locks 包下面。AQS为Java的并发包提供了强大的同步支持。通过内置的FIFO队列来完成资源获取线程的排队工作,并且利用一个被volatile关键字修饰的int类型的变量表示同步状态。AQS 为构建锁和同步器提供了一些通用功能的实现,许多同步类实现都依赖于它,如常用的ReentrantLock、Semaphore等。

qq_35716689的博客 1万+

Android Treble 简介

上面两段话来自官网上,Android 8.0 重新设计了 Android 操作系统框架(在一个名为“Treble”的项目中),以便让制造商能够以更低的成本更轻松、更快速地将设备更新到新版 Android 系统。本文主要整理一下Android Treble框架。

私房菜 9275

华为OD机试真题2026双机位C卷 Java&Go 实现【用户入网定期复评】

用户开立支付账户,在开户完成后立即对用户进行一次风险模型评级,输出一个评分风险,评分范围1-100,等区间的分为5个等级,1-19为等级1,20-39为等级2,40-59为等级3,60-79为等级4,80-100为等级5,系统每天会任务对全量入网用户进行计算复评时间,输出评分给出新的风险等级。复评规则如下:

MISAYAONE的博客 149

JAVA并发编程AQS原理剖析

很多小朋友面试时候,都会被面试官问:说一下AQS原理。面对并发编程基础和面试经验,拉丁解牛专栏采用通俗简洁无废话无八股文方式剖析原理和源码,让天下没有难啃的技术。

拉丁解牛技术专栏 1475

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

Java并发之队列同步器AQS原理

独占锁相当于同一时间只有一个线程能去占用它,然后必须等到它释放锁之后,下一个线程才能去获取AQS的实现采用的是模板模式,对外提供三类方法用于实现一个同步组件使用3个方法访问或修改同步状态(state)final intgetStatefinal voidsetState(使用CAS设置当前状态,能够保证状态设置的原子性)CAS全称是Compare And Swap,是一种用于在多线程环境下实现同步功能的机制.CAS操作包括三个操作数:内存位置,预期数值和新值。

2301_79567474的博客 921

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

谈谈你对AQS的了解

前言:今天无意中看到了"中华石衫"大佬的公众号的文章,觉得很不错,就摘录下来了。 1.写在前面: 这篇文章,我们来聊聊面试时一个比较有杀伤力的问题:聊聊你对AQS的理解? 之前有同学反馈,去互联网公司面试,面试官聊到并发时就问到了这个问题。当时那位同学内心估计受到了一万点伤害。。。因为首先,很多人可能连AQS是什么都不知道。或者仅仅是听说过AQS这个名词,但是可能连全称怎么拼写都不知道。更有...

oldshaui的博客 7307

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

Java-并发编程-AQS

AQS 的全称为,翻译过来的意思就是抽象队列同步器。这个类在包下面。AQS 就是一个抽象类,主要用来构建锁和同步器。AQS 为构建锁和同步器提供了一些通用功能的是实现,因此,使用 AQS 能简单且高效地构造出应用广泛的大量的同步器,比如我们提到的Semaphore,其他的诸如等等皆是基于 AQS 的。使用者继承并重写指定的方法。将 AQS 组合在自定义同步组件的实现中,并调用其模板方法,而这些模板方法会调用使用者重写的方法。这和我们以往通过实现接口的方式有很大区别,这是模板方法模式很经典的一个运用。

Java程序员的知识博客 1285

Java高并发核心:AQS原理与面试必考点解析

文章深入解析Java高并发核心机制AQS(AbstractQueuedSynchronizer)的工作原理,从锁的状态管理、队列结构到加锁与释放流程逐一剖析,并结合实际案例讲解如何自定义同步器。此外,文章还总结了AQS在面试中的必考点,包括线程协调机制、公平锁与非公平锁的区别以及常见问题的解决方案,帮助读者全面掌握这一高并发核心技术,轻松应对技术面试挑战...

yanpeng2580的博客 664

Java并发-AQS及各种Lock锁的原理

什么是AQS AQS是AbustactQueuedSynchronizer的简称,它是一个Java提高的底层同步工具类,用一个int类型的变量表示同步状态,并提供了一系列的CAS操作来管理这个同步状态。AQS的主要作用是为Java中的并发同步组件提供统一的底层支持,例如ReentrantLock,CountdowLatch就是基于AQS实现的,用法是通过继承AQS实现其模版方法,然后将子类作为同...

不瘦十斤不换名字 3万+

AQS核心原理分析《上》

本篇文章是关于并发编程中AQS的简介和组成知识记录,由于篇幅原因,独占模式和共享模式将会在下篇记录。

小威的博客 5723

深入解析AQS原理:Java并发编程的核心

AQS(AbstractQueuedSynchronizer)是Java并发包()的核心组件,用于构建锁和其他同步器的基础框架。它通过一个FIFO队列管理线程的排队和唤醒机制,提供了独占模式和共享模式的同步功能。AQS的核心功能🧩同步状态管理:通过state变量管理锁的状态🛠️线程排队:通过FIFO队列管理等待线程🔒条件支持:支持条件变量(Condition)的实现AQS的应用Semaphore。

KBkongbaiKB的博客 1184

Java多线程编程-(12)-Java中的队列同步器AQS和ReentrantLock锁原理简要分析

一、Lock接口在上一篇文章中: Java多线程编程-(5)-使用Lock对象实现同步以及线程间通信 介绍了如何使用Lock实现和synchronized关键字类似的同步功能,只是Lock在使用时需要显式地获取和释放锁,synchronized实现的隐式的获取所和释放锁。虽然Lock它缺少了(通过synchronized块或者方法所提供的)隐式获取释放锁的便捷性,但是却拥有了锁获取与释放的可操作性、

徐刘根的博客 4153

JAVA面试宝典-《并发编程必考:synchronized与AQS原理》

本文深入解析Java并发编程中的两种核心同步机制:synchronized和AQS。首先通过银行转账等案例说明线程安全的重要性,详细剖析synchronized的使用方式及其底层实现原理,包括偏向锁、轻量级锁和重量级锁的升级过程。然后解读AQS框架的核心机制,包括状态管理、CLH队列和加解锁流程,并给出自定义锁实现案例。最后对比两者的实现方式、性能特点和使用场景,提供常见面试问题解答,建议根据实际需求合理选择同步方案,掌握这些技术对开发高性能并发程序至关重要。

qq_43414012的博客 1081
上一篇: 【EasyExcel】后台开发如何利用EasyExcel优雅的操作Excel
下一篇: 字节跳动后台Java2020面试真题(含手写代码及设计题)
少侠露飞
博客等级 码龄9年 214粉丝 117原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值