并发编程之AQS(抽象队列同步器)共享锁

Java并发编程之AQS AQS是AbstractQueueSynchronizer是缩写,其意思就是抽象队列同步器。原理就是通过一个FIFO队列维护一个state同步状态值,只需要继承该抽象类重写对应的方法便可实现一套线程同步机制。AQS抽象类在java.util.concurrent.locks包下定义。如上图所示,AQS属于一个抽象类,主要是用来构建锁结构以及同步器的一个抽象类。AQS继承了AbstractOwnableSynchronizer,其作用为设置和获取独占锁的拥有者线程方法。 阅读详情

前言

在上一遍笔记中详细介绍了AQS抽象队列同步器的独占锁,在AQS中,独占锁的实现是使用ReentrantLock来实现的,那么共享锁在AQS中都有哪些是实现呢?在AQS中,共享锁的实现比较常用的有Semaphore,CountDownLatch以及CyclicBarrier,这三个都是AQS中共享锁的实现,其中Semaphore是信号量的意思,并且是可重复使用的,那么这三个在AQS中是如何标记是共享锁的,在AQS的双向链表Node数据结构中有两个属性如下:

/** Marker to indicate a node is waiting in shared mode */
static final Node SHARED = new Node();
/** Marker to indicate a node is waiting in exclusive mode */
static final Node EXCLUSIVE = null;

其中SHARED表示共享锁,而EXCLUSIVE 表示是独占锁,在上一篇笔记中已经介绍了独占锁EXCLUSIVE ,独占锁的逻辑看懂了,那么共享锁的逻辑也就很简单了。
我们记得AQS同步队列有一个非常重要的特点就是永远都是都head的下一个结点唤醒执行,head出队,下一个结点作为head结点,也就是说head结点永远是空的,head存在的意义就是唤醒下一个结点线程,然后head出队,下一个被唤醒的结点又更新成空的,因为它在执行,所以正在执行的线程的Node是空的,它是用来唤醒下一个线程的。

Semaphore信号量

Semaphore是java中线程同步的另一手段,我们知道ReentrantLock是用来线程中保证线程数据安全的,但是因为它是独占锁,并且使用同步队列来唤醒,而Semaphore是可以用于并发访问的线程数,来控制线程访问的数量,比如我们的一个文件读写只能由多少个并发线程来访问就可以通过Semaphore来实现,简单来说就是Semaphore定义了获取资源的线程数量,比如你设置的是1,那么就只能用于一个线程并发访问,它执行完成过后会回收,然后再通知下一个线程进行处理,当只有一个Semaphore信号量的时候,和ReentrantLock能达到差不多的一个效果,但是实现上有一定的区别,举个简单的例子,我们去电影院看电影买票的时候,柜台如果开了3个窗口进行买票,比喻买票的100个人是线程,那么 这100个线程都要在这3个窗口买,这3个窗口一次性只能接受3个线程进行处理,其他线程进入AQS的等待队列,当前面3个人每个人买完过后通知到下一个线程进行处理,也就是唤醒线程开始买票,唤醒下一个线程购票的工程中,下个线程如果获取锁成功,那么会进行传播,依次唤醒等待队列的线程,我们先来看个例子:

public class SemaphoreTest {

    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3);
        for(int i = 1; i<= 5;i++){
            new Thread(()->{
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+" 开始 ");
                    Thread.sleep(3000);
                    System.out.println(Thread.currentThread().getName()+" 结束 ");

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    semaphore.release();
                }

            },"Thread"+i).start();
        }
    }
}

上面的程序的大概思路是我开了3个窗口,有5个人过来买票,那么第一次肯定只有3个人买到票,只有等每个窗口买票的线程走了以后通知到下一个线程,下个线程才能买票.

Semaphore 的构造

public Semaphore(int permits) {
    sync = new NonfairSync(permits);
}
NonfairSync(int permits) {
    super(permits);
}
Sync(int permits) {
    setState(permits);
}

上面的构造方法其实只反应了两个信息
1.Semaphore使用的非公平;
2.将系统的资源设置为permits,也就是只有permits个线程能够获取到锁(state在上一篇笔记已经说了,就是锁的资源).

semaphore.acquire()

获取资源所,我们就以上面的代码示例来看,我们有5个线程启动,一步一步来分析下,首先先看静态代码

public void acquire() throws InterruptedException {
    //获取资源锁,默认都是1
    sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
            //判断用户线程是否设置了中断
    if (Thread.interrupted())
        throw new InterruptedException();
        //tryAcquireShared最终会调用到nonfairTryAcquireShared
        //下面分析了,如果返回值小于0,则证明没有资源了,需要调用
        //doAcquireSharedInterruptibly入队了,在队列中等待被唤醒
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}
//获取资源锁
final int nonfairTryAcquireShared(int acquires) {
    for (;;) {
        //首先得到state,state初始化的时候是3,所以这里available=3
        int available = getState();
        //获取了资源锁1,然后剩余是为2
        int remaining = available - acquires;
        //这边不要看return,主要看if的两个条件,首先看资源是否小于0
        //或者cas修改资源数为remaining ,最后返回资源数,如果资源数》0的
        //那么获取锁成功
        if (remaining < 0 ||
            compareAndSetState(available, remaining))
            return remaining;
    }
}

doAcquireSharedInterruptibly(int arg)

下面这个方法就是因为资源不够了,需要入队,进入AQS的等待同步队列

private void doAcquireSharedInterruptibly(int arg)
    throws InterruptedException {
        //这个方法和独占锁的添加的Waiter一样的意思,唯一不一样的就是
        //这边添加的是共享锁,ReentrantLock添加剂的是独占锁
        //添加到同步等待队列过后,比如head结点是head,那么
        //head.next=node ,node.prev=head
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        //开启循环,这里即是阻塞处,也是被唤醒处
        for (;;) {
            //这里得到的是head,因为node.prev就是head
            final Node p = node.predecessor();
            //这里肯定是相等的
            if (p == head) {
                //再尝试获取锁,如果获取到锁,然后它自己进行传播setHeadAndPropagate
                //也就是说获取到锁过后,自己出队了还要传播到下一个线程
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    //如果获取到锁,进行传播
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
            }
            //如果上面没有获取到锁,这里先修改waitStatus=-1,然后
            //阻塞当前线程,修改的是node.next的线程的waitStatus=-1
            //调用parkAndCheckInterrupt进行阻塞
            //被唤醒的时候也在这里被唤醒
            //for循环第一个修改的是waitStatus=-1,
            //第二次调用parkAndCheckInterrupt 阻塞线程
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

上面就是semaphore加锁过程,整个过程算是比较简单的,把独占锁的代码看懂过后,这里就非常简单了,就是在初始化对象的时候传入信号多少个,也就是只能允许多少个资源同时进行,然后加锁的过程就是每个线程获取一个资源,直到资源用完过后进入等待队列,下面来分析下资源释放的源码;

semaphore.release()

//资源释放,简单理解就是解锁,将自己所用于的资源还回去
public void release() {
    //每个线程默认都一次只释放一个资源
    sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
    //尝试释放资源,如果成功,进入doReleaseShared
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}
//资源释放方法
protected final boolean tryReleaseShared(int releases) {
    for (;;) {
        //首先回去目前的资源数量,state是在初始化的时候通过传入的资源数
        //设置为state,比如state=3
        int current = getState();
        //资源释放过后,那么原有的资源需要+1,也就是资源还回去,下个线程
        //就可以获取到资源
        int next = current + releases;
        if (next < current) // overflow
            throw new Error("Maximum permit count exceeded");
            //通过cas将当前的资源数修改到next数量上,
            //这里为什么要使用cas呢?因为是多线程,要保证数据安全
            //并且对其他线程可见
        if (compareAndSetState(current, next))
            return true;
    }
}
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 (;;) {
        //如果启动的线程数大于了state,那么这里肯定有阻塞队列
        //其实这里不是出队,只是唤醒head.next线程
        Node h = head;
        //判断这里的head不为空,这个条件我们都能理解
        //但是h!=tail为什么要这么写呢?因为AQS同步队列有个特点
        //在上一篇笔记中我已经写了,唤醒的永远是头部节点的下一个节点
        //而下一个节点的waitStatus永远是0,当下一个节点被唤醒过后
        //下一个节点的线程设置为null,waitStatus又等于-1,表示
        //它执行完了又可以唤醒下一个节点了,这是独占锁的逻辑
        //而共享锁的逻辑是通过传播实现的唤醒,待会儿分析传播的代码
        //所以这里要判断是否是最后一个节点,因为最后一个节点已经
        //运行到这里了,同步队列中已经没有线程要唤醒的了,而且被唤醒的
        //的节点的waiteStatus是0,因为唤醒的是head.next,所以这里就算
        //进了循环也毫无意义
        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);
            }
            //对于共享锁来说,这里永远不会进,因为到这里,只要不是tail
            //节点,基本上ws都是-1
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;                // loop on failed CAS
        }
        if (h == head)                   // loop if head changed
            break;
    }
}

if (h != null && h != tail) 这个判断的解释,我这里贴一下,因为上面的代码块无法标记重要部分
//判断这里的head不为空,这个条件我们都能理解
//但是h!=tail为什么要这么写呢?因为AQS同步队列有个特点
//在上一篇笔记中我已经写了,唤醒的永远是头部节点的下一个节点
//而下一个节点的waitStatus永远是0,当下一个节点被唤醒过后
//下一个节点的线程设置为null,waitStatus又等于-1,表示
//它执行完了又可以唤醒下一个节点了,这是独占锁的逻辑
//而共享锁的逻辑是通过传播实现的唤醒,待会儿分析传播的代码
//所以这里要判断是否是最后一个节点,因为最后一个节点已经
//运行到这里了,同步队列中已经没有线程要唤醒的了,而且被唤醒的
//的节点的waiteStatus是0,因为唤醒的是head.next,所以这里就算
//进了循环也毫无意义

unparkSuccessor(h)

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.
     */
     //到这里已经要唤醒线程了,唤醒的线程是node.next
     //所以目前的head待会儿是要出队的,在AQS中,0是表示正在执行的
     //线程,而且head也是正在执行的线程(可以这样认为),而-1是
     //要去唤醒它的下一个节点(线程),所以这里修改成0
     //其实笔者认为这里可以不修改也可以,反正待会儿要出队
     //但是这是要给标准 
    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;
    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);
}

代码执行到这里的时候,其实已经开始唤醒第四个线程了,这个时候我们的第三个线程还阻塞在for循环哪里在,等待被唤醒,当线程1执行了 LockSupport.unpark(s.thread);,第四个线程就已经被唤醒,我们看第四个线程别阻塞的位置
在这里插入图片描述
第四个线程被阻塞到红色方框这里,所以这里唤醒了,我们分析下被唤醒的后的代码,被唤醒过后再次循环,所以看源码分析如下:

for (;;) {
    //每个被唤醒的线程的prev节点肯定是head节点,这个不用多说,
    //因为每次入队都是尾节点,队列是FIFO,所以只要轮到出队,那么node.prev
    //肯定是head
    final Node p = node.predecessor();
    if (p == head) {
        //这里尝试获取所,因为线程1已经释放了锁,所以这个时候state=1
        //所以这里获取锁肯定是能够成功的
        int r = tryAcquireShared(arg);
        if (r >= 0) {
            //这里就是重点,这里就是唤醒的传播性,我没有去看过网上
            //各大博客有没有说这里是唤醒的传播,我是这样理解的
            //这里的node=当前线程的node
            setHeadAndPropagate(node, r);
            //p.next其实就是node,在方法setHeadAndPropagate
            //中设置了node.prev=null,结合这里的p.next=null
            //其实就是出队,head出队,执行的线程设置线程为空,
            //作为下一个head存在(node)
            p.next = null; // help GC
            failed = false;
            return;
        }
    }

semaphor的源码大概分析完了,就是给定的一定资源数量,然后根据资源数量的线程获取锁,然后执行,没有获取资源的线程就进入等待队列,然后获取资源的线程处理完了去队列里面唤醒等待的线程,然后唤醒线程有个特性,就是传播性,被唤醒的线程如果是共享锁,那么它会传播下去,依次唤醒在等待队列中的线程。semaphor的总体流程如下:
在这里插入图片描述

实列分析

启动5个线程,资源数为3,就以文章刚开始的那段程序,我们debug启动
在这里插入图片描述

入队

在这里插入图片描述

解锁出队

在这里插入图片描述
在资源数为3,启动5个线程执行的过程中,执行流程如上图,因为资源数为3,所以5个线程中只有前3个线程能够获取到锁,第四个和第五个线程进入同步队列中进行阻塞,前3个线程中,只要执行完成自己的业务逻辑过后,就会通知队列中处于阻塞等待的线程,当唤醒了第一个线程过后,第一个线程进行出队执行,在出队执行的过程中,还是判断是否是共享锁,如果是共享锁,那么还是通知下一个被阻塞的线程,依次唤醒后续被阻塞的线程,但是有个前提条件就是被唤醒的线程只有获取锁成功过后执行执行自己线程的业务逻辑之前,才会去唤醒下一个阻塞的线程,唤醒执行也就意味着出队,当出队的线程只有一个的时候,则不需要唤醒了,因为这个时候队列已经到了tail了,而tail目前就是被上一个线程唤醒的,所以到这里就唤醒了所有的线程执行了。而每当一个线程执行完成过后,会把资源还回去,也就是说比如资源是3,当线程获取了线程执行权限过后,当前资源数-1,即为2,如果这个线程执行完成在release过后,资源数也就是state又会+1,这个和独占锁优点类似,独占锁是初始资源数为0,获取了锁为1,来获取锁的线程必须要判断资源数为0才能获取锁,所以独占锁和semaphor共享锁还是有一定的区别的。

CountDownLatch

CoutDownLatch是一个闭锁,通过名字就可以知道是一个类似于计数器的共享锁,初始化需要设置一个计数器大小,
当计数器为0的时候,释放锁,也就是说线程获取锁过后,需要线程自己调用countDown()方法将计数器减一,
CoutDownLatch借助了AQS的实现,实现很简单,我这边就不分析源码了,源码都非常简单,CoutDownLatch使用场景比较常见的就是永远闭锁的场景,它和semaphor最简单的区别就是semaphor定义了的资源数,那么如果启动了线程大于了资源数是需要进行阻塞的,而CoutDownLatch是不需要的,CoutDownLatch如果计数器为0了,也会让线程通过,还是简单看下代码实现:

countDown()

public void countDown() {
    //减去计数器其实就是释放锁,和semaphor还是有区别,countDownlatch来就释放资源
    //其实原理不一样而已
    sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
    //这里就是把计数器state -1,如果为0,返回false,其他返回true
    if (tryReleaseShared(arg)) {
        //进入了这个方法也没什么用,这里是释放资源,队列都没有,何来释放
        doReleaseShared();
        return true;
    }
    return false;
}
protected boolean tryReleaseShared(int releases) {
        // Decrement count; signal when transition to zero
        for (;;) {
            int c = getState();
            if (c == 0)
                return false;
            int nextc = c-1;
            if (compareAndSetState(c, nextc))
                return nextc == 0;
        }
    }
}

await

public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}
//看懂这行代码就可以了,就是资源数如果不是0,那么阻塞,阻塞的代码在doAcquireSharedInterruptibly
 //和semaphor的一样的逻辑,所以当线程调用countdown过后,当state=0的时候就释放了
 //释放的逻辑和代码和在doAcquireSharedInterruptibly
 //和semaphor一模一样,没有任何区别
protected int tryAcquireShared(int acquires) {
    return (getState() == 0) ? 1 : -1;
}

await借助了semaphor的实现,其实这里非常简单,我们一定要理解countdownLatch的实现原理,它是用来做闭锁使用的,简单来说就是比如我们要启动5个线程去处理,那么我们设置计数器为5,那么调用了await过后,只有等到这5个线程执行完成过后,那么才会通过await,所以countDownLatch的阻塞其实是阻塞主线程的,而semaphor是阻塞子线程的,也就是说在执行的时候,await阻塞了主线程,那么所有的子线程在调用countDown过后,计数器-1,当计数器为0的时候,那么主线程就会释放了,所以countDownLatch永远只有2个结点 head 和tail,tail是主线程结点。
所以只要知道countdownlatch是用于什么场景即可以及它的简单实现原理;所以CountDownLatch用于闭锁的场景比较多,感觉就和thead.join()有点类似 ,就是等待线程执行完成,但是countDownlatch是阻塞主多线程的执行结果的。

流程图

在这里插入图片描述
简单粗略的画了下countDownlatch的执行过程,这个比较简单,理解原理和使用即可

【Java并发编程】4. 一文搞定JUC半壁江山 AQS 文章目录AQSAQS了解AQS中模版模式AQS重要方法模板方法独占式获取共享式获取独占式释放共享式释放子类需实现方法独占式获取独占式释放共享式获取共享式释放 AQS AQS了解 在JDK中我们一般用AQS来构建跟实现显示锁,语言层面我们一般用Syn来实现。AQS(AbstractQueuedSynchronizer 抽象队列同步器),在上一文中被广泛使用。比如CountDownLatch,Thre... 阅读详情

相关推荐

SAR ADC设计——SAR Logic原理

SAR ADC设计——SAR Logic原理 参考:集成电路设计实践 SAR ADC 清华大学 李福乐 文章目录SAR ADC设计——SAR Logic原理一. Overview二. TSPC(True Single Phase Clock)三. SC_Gen(Sequential Control Generator)四. LATCH_Dynamic五. 时序控制逻辑总结六. SW产生七. DO产生八. GT产生九. SAR Logic模块总结 一. Overview 输入:CKC,CompB,SAMPL

夏风喃喃的博客 1万+

并发多线程之AQS源码分析(上)

目录AQS的实现原理AQS的队列实现NodeAQS的核心属性和方法 在JUC原理一篇了解了JUC的都是围绕着AbstractQueuedSynchronizer(简称AQS)实现的,也就是说AbstractQueuedSynchronizer为JUC的核心,本篇就是探索AbstractQueuedSynchronizer的源码。 AQS的实现原理 在开始阅读源码之前再来梳理一下JUC的原理,那么在阅读源码时可以提高效率。 AQS的两个队列 在AQS中存在两个队列用于对线程的状态保存和记录,分别是同步队列和条

binga的博客 416

量化策略分享之首板低开

这个量化策略的核心是选择在低位、首次涨停后次日低开的股票,开盘时买入,上午有盈利就卖出,没有就拿到尾盘在卖出;策略优点在于选择的股票在低位,风险较低,并且刚启动首板,有大资金介入,第二天能快速跑路。

qq_29301281的博客 1469

并发编程学习笔记之九—并发工具可重入锁ReentrantLock源码分析

在解决并发的手段中,互斥锁是最常见的手段之一。在Java实现管程的方式已经有了synchronized,但是为什么还会在并发工具包提供Lock接口呢?带着疑问我们来剖析下并发工具包中Lock锁的实现原理。 下面以ReentrantLock为例分析下实现原理: 如何保证加锁和解锁的状态 简述锁机制1.主要通过一个volatile修饰的int变量state来表示锁数量,0代表目前无线程获得锁,大...

anyway8090的博客 266

LangChain4j 开发

市场上主流的 Java 调用大模型的工具库有两种:LangChain4j 是一个 面向 Java 的开源框架,用于构建 基于大语言模型(LLMs) 的应用程序。它是 LangChain(Python 生态中非常流行的 LLM 应用框架)的 Java 实现,旨在让 Java 开发者能够轻松地集成和使用各种 LLM 服务,快速开发 智能聊天机器人、智能搜索、文档问答、Agent系统 等 AI 驱动的应用。官网文档:https://docs.langchain4j.dev/。引入依赖(JDK17 及以上): 构建

m0_62943934的博客 2795

USB2.0基础知识总结

目录 1、协议层: 1.1、域分类 1.2 、USB传输方式: 1、协议层: 域和包定义:包是一次传输的单位,每个包由多个域组成; USB中是按照LSB进行字节传输; 1.1、域分类 1.1.1: 同步域(SYNC field) 所有的包都开始于同步域,在低速和全速通信中SYNC field用7个0和1个1来表示。 由于USB2.0使用了NRZI编码(0用翻转来表示,1用不翻转表示),所以00000001就是上图那样。 ...

qq_32938605的博客 4410

聊聊高并发(二十一)解析java.util.concurrent各个组件(三) 深入理解AQS(一)

AQS是AbstractQueuedSynchronizer的缩写,AQS是Java并包里大部分同步器的基础构件,利用AQS可以很方便的创建锁和同步器。它封装了一个状态,提供了一系列的获取和释放操作,这些获取和释放操作都是基于状态的。它的基本思想是由AQS负责管理同步器类中的状态,其他的同步器比如可重入锁ReentrantLock, 信号量Semaphore基于各自的特点来调用AQS提供了基础能力

ITer_ZC的专栏 6860

VisDrone2019数据集实战:从下载到YOLO格式转换的完整指南

本文提供了一份完整的VisDrone2019数据集实战指南,详细介绍了从数据集下载、理解其独特的标注格式,到转换为YOLO模型可直接训练格式的全过程。通过清晰的代码示例和步骤讲解,帮助读者掌握处理复杂无人机视觉数据集的核心技能,为基于YOLO的目标检测模型训练做好准备。

weixin_29165725的博客 385

并发编程理论 - AQS之双向链表和条件队列数据结构

目录 一、双向链表的Node节点结构 二、AQS结构梳理 三、state与双向链表模型 四、核心模板方法分析 1、tryAcquire 2、tryRelease 3、tryAcquireShared 4、tryReleaseShared 根据上一篇理解了Doug Lea设计AQS的意图和思路,AQS作为抽象的模板方法在其中完成了大量的同步方法的封装,分为排他和共享两个模式。所以我们的理解思路就是先看看state值与虚拟双向链表【以及Condition...

it_lihongmin的博客 2114

Ta-Lib源码解析(三):蜡烛图指标 (Candlestick Indicator) #(附Python重构代码和示意图)(补充中)

蜡烛图指标为识别K线形态的指标,笔者认为重点在于定义方式所提供的思路,背后的投资逻辑可以借鉴但不建议直接参考文档的翻译内容和源码逻辑存在不一致的现象,笔者分别列出中文百科中的定义和源码实际逻辑,供大家参考技术分析理论中的一些模糊词,ta-lib中给出了精确的定义源码

量化就是探索生活 3254

0基础学习diffusion_model扩散模型【易理解的公式推导】Denoising Diffusion Probabilistic Models

去噪扩散概率模型。本文是笔者在学习扩散模型时的一些笔记与心得,在公式推导过程中能够保证自己是一步一步去推导并且理解了的。概述是我认为比较重要的部分能够帮助理解公式的推导,具体的公式推导在每一小节中进行。1、扩散过程:如上图所示,扩散过程为从右到左X0→XTX0​→XT​的过程,表示对图片逐渐加噪,且Xt1X_{t+1}Xt1​是在XtX_{t}Xt​加躁得到的,其只受XtX_{t}Xt​的影响,因此扩散过程是一个马尔科夫过程。X0X_0。

zik的博客 2402

ReentrantLock源码之一lock方法解析(锁的获取)

一、前言    ReentrantLock是JDK1.5引入的,它拥有与synchronized相同的并发性和内存语义,并提供了超出synchonized的其他高级功能(例如,中断锁等候、条件变量等),并且使用ReentrantLock比synchronized能获得更好的可伸缩性。     ReentrantLock的实现基于AQS(AbstractQueuedSynchronizer)和L...

gaozhangl的博客 297

YOLOv11改进策略【损失函数篇】| 通过辅助边界框计算IoU提升检测效果(Inner_GIoU、Inner_DIoU、Inner_CIoU、Inner_EIoU、Inner_SIoU)

为了弥补现有 IoU 损失在不同检测任务中泛化性弱和收敛速度慢的问题,·Inner-IoU·通过引入比例因子 “ratio” 来控制辅助边界框的尺度大小,利用不同尺度的辅助边界框来计算损失,从而加速边界框回归过程。

Limiiiing的博客 3893

AQS核心原理分析《上》

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

小威的博客 5723

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

Java线程并发

线程安全问题解决方法

蔡林浩的博客 311

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

面试之【多线程】

AQS是Java并发的一个重要组件,是一个抽象类,提供了线程同步的底层实现机制,实现线程同步和互斥操作,提供了两种主要的锁机制,分别是排它锁和共享锁。CAS:Compare and Swap)是一种多线程同步的机制,通常用于实现无锁算法,特别是在多线程环境下对共享变量进行操作。共享锁:允许多个线程同时获的锁资源,但同一时刻只有一个线程可以获取到所得拥有权,其他线程需要等待该线程释放锁。排它锁:也称为独占锁,在多个线程中竞争同一共享资源时,同一时刻只允许一个线程访问该共享资源。ABA问题:配合版本号。

吉光片羽的博客 322

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

CountDownLatch源码浅析

文章目录CountDownLatch热身源码解析创建CountDownLatch对象countDown CountDownLatch也是juc包下的工具类,用于实现线程同步。 CountDownLacth 和juc(java.util.concurrent)包下的其他工具类一样,内部存在一个继承了AQS(AbstractQueuedSynchronizer)的内部类Sync。通过Sync来操作同步...

my博客 205

抽象队列同步器 AQS

抽象队列同步器AQS

scj1022的博客 1547

并发编程学习之AQS抽象队列同步器

一、简介 AQS是AbstractQueuedSynchronizer的简写,翻译过来就是:抽象队列同步器。AbstractQueuedSynchronizer在java.util.concurrent.locks包中,声明如下: public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchroni...

weixiaohuai的博客 716

并发——浅谈AQS(抽象队列同步器)

AQS的全称是AbstractQueuedSynchronizer,抽象队列同步器。这是JUC(java.util.concurrent)包下的一个抽象类。它为Java中的并发同步提供了一个基础的框架。使得并发编程变得更容易、更可靠。AQS抽象类内部主要维护了上图四个属性:内部类Node、头节点head、尾节点tail和同步的属性标志state。前三个属性共同组成了一个特殊且完整的FIFO的双向链表,AQS正是基于这个链表管理线程的阻塞和唤醒。state变量则帮助对同步状态进行控制。

m0_59382054的博客 810

AQS(抽象队列同步器)详解

同步锁的本质 —— 排队 同步的方式:独享锁 - 单个队列窗口,共享锁 - 多个队列窗口; 抢锁的方式:插队抢(非公平锁)、先来后到抢(公平锁); 没捡到锁的处理方式:快速尝试多次(CAS自旋锁)、阻塞等待; 唤醒阻塞线程的方式(叫号器):全部通知、通知下一个; AQS概念 资源占用流程 手写抽象队列同步器 import java.util.Iterator; import java.util...

maxleader的博客 733

学Java线程,你不知道什么是AQS?一文带你了解Java多线程同步的灵魂

AQS 定义了同步器的基本操作,如获取、释放和状态管理,并提供了一个等待队列来管理等待资源的线程,解决了在实现同步器时涉及的大量细节问题,例如自定义标准同步状态、FIFO 同步队列。

fumeidonga的博客 5612

【并发】5、抽象队列同步器AQS应用ReentrantLock

AQSCASReentrantLockAQS源码分析 Lock的特性: 可重入 这个特性就是加了几次锁也要释放几次锁 synchronized也有可重入性 公平性与非公平性 实现锁的核心: CAS 保证加锁永远只有一个线程能够成功 LockSupport 对线程阻塞和唤醒 自旋 cas加锁失败,则这些线程就要自旋,阻塞住 就不占用cpu资源 queue 放阻塞的那些线程,用队列是因为队列的FIFO可以保证公平性 CAS CAS Compare and swap 能够保证不管并发有多高,都能保证

flora 378

AQS系列文章 | 并发编程基石:为什么需要AQS?

全称:AbstractQueuedSynchronizer(抽象队列同步器)身份包中的抽象类职责:为构建锁和同步器提供基础设施(如线程排队、状态管理)AQS就像乐高积木:你不需要从零造轮子,只需组合它提供的基础模块(状态管理、线程排队),就能搭建出ReentrantLock、Semaphore等强大的同步工具。下一篇预告《AQS核心原理:状态与队列机制》——我们将拆解AQS内部的神秘“计数器”和“排队区”,用动画演示线程如何有序等待!

ibigboy 827

并发编程-15并发容器(J.U.C)核心 AbstractQueuedSynchronizer 抽象队列同步器AQS介绍

文章目录J.U.C脑图J.U.C核心AQS简介AQS底层数据结构AQS特点AQS同步组件之 CountDownLatch 闭锁AQS同步组件之 Semaphore 信号量AQS同步组件之 CyclicBarrier 同步屏障AQS同步组件之 ReentrantLock与锁 J.U.C脑图 为了体现出AQS和线程池的重要性,上图单独将AQS和线程池拿出来了。 J.U.C的构成如下: J.U...

小工匠 2万+

手写Lock锁与AQS抽象队列同步器

手写Lock锁 锁的实现需要: 两个方法:lock()、unlock() 两个组件:锁的持有者、等待队列 代码: public class CustomLock implements Lock{ //当前前程 AtomicReference<Thread> current = new AtomicReference(); //等待队列 LinkedBl...

Der_Dream的博客 363
上一篇: 并发编程之AQS(抽象队列同步器)独占锁
下一篇: 并发编程之条件队列CyclicBarrier(栅栏)
Life  journey
博客等级 码龄17年 1万+粉丝 80原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值