当前位置: 首页 > news >正文

七、JUC并发工具

文章目录

  • JUC并发工具
  • CountDownLatch应用&源码分析
    • CountDownLatch介绍
    • CountDownLatch应用
    • CountDownLatch源码分析
      • 有参构造
      • await方法
      • countDown方法
  • CyclicBarrier应用&源码分析
    • CyclicBarrier介绍
    • CyclicBarrier应用
    • CyclicBarrier源码分析
      • CyclicBarrier的核心属性
      • CyclicBarrier的有参构造
      • CyclicBarrier中的await方法
  • Semaphone应用&源码分析
    • Semaphore介绍
    • Semaphore应用
    • Semaphore源码分析
      • Semaphore的整体结构
      • Semaphore的非公平的获取资源
      • Semaphore公平实现
      • Semaphore释放资源
    • AQS中PROPAGATE节点
      • 掌握JDK1.5-Semaphore执行流程图
      • 分析JDK1.8的变化

JUC并发工具

CountDownLatch应用&源码分析

CountDownLatch介绍

CountDownLatch就是JUC包下的一个工具,整个工具最核心的功能就是计数器。
如果有三个业务需要并行处理,并且需要知道三个业务全部都处理完毕了。
需要一个并发安全的计数器来操作。
CountDownLatch就可以实现。
给CountDownLatch设置一个数值。可以设置3。
每个业务处理完毕之后,执行一次countDown方法,指定的3每次在执行countDown方法时,对3进行-1。
主线程可以在业务处理时,执行await,主线程会阻塞等待任务处理完毕。
当设置的3基于countDown方法减为0之后,主线程就会被唤醒,继续处理后续业务。
在这里插入图片描述
当咱们的业务中,出现2个以上允许并行处理的任务,并且需要在任务都处理完毕后,再做其他处理时,可以采用CountDownLatch去实现这个功能。

CountDownLatch应用

模拟有三个任务需要并行处理,在三个任务全部处理完毕后,再执行后续操作。
CountDownLatch中,执行countDown方法,代表一个任务结束,对计数器 -1。
执行await方法,代表等待计数器变为0时,再继续执行。
执行await(time,unit)方法,代表等待time时长,如果计数器不为0,返回false,如果在等待期间,计数器为0,方法就返回true
一般CountDownLatch更多的是基于业务去构建,不采用成员变量。

public class TestCountDownLatch {static ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(3);static CountDownLatch countDownLatch = new CountDownLatch(3);public static void main(String[] args) throws InterruptedException {System.out.println("主业务开始执行");sleep(1000);executor.execute(TestCountDownLatch::a);executor.execute(TestCountDownLatch::b);executor.execute(TestCountDownLatch::c);System.out.println("三个任务并行执行,主业务线程等待");// 死等任务结束// countDownLatch.await();// 如果在规定时间内,任务没有结束,返回falseif (countDownLatch.await(10, TimeUnit.SECONDS)) {System.out.println("三个任务处理完毕,主业务线程继续执行");} else {System.out.println("三个任务没有全部处理完毕,执行其他的操作");}}private static void a() {System.out.println("A任务开始");sleep(1000);System.out.println("A任务结束");countDownLatch.countDown();}private static void b() {System.out.println("B任务开始");sleep(1500);System.out.println("B任务结束");countDownLatch.countDown();}private static void c() {System.out.println("C任务开始");sleep(2000);System.out.println("C任务结束");countDownLatch.countDown();}private static void sleep(long timeout) {try {Thread.sleep(timeout);} catch (InterruptedException e) {e.printStackTrace();}}
}

CountDownLatch源码分析

保证CountDownLatch就是一个计数器,没有什么特殊的功能,查看源码也只是查看计数器实现的方式。
发现CountDownLatch的内部类Sync继承了AQS,CountDownLatch就是基于AQS实现的计数器。
AQS就是一个state属性,以及AQS双向链表。
猜测计数器的数值实现就是基于state去玩的。
主线程阻塞的方式,也是阻塞在了AQS双向链表中。

有参构造

就是构建内部类Sync,并且给AQS中的state赋值。

// CountDownLatch的有参构造
public CountDownLatch(int count) {// 健壮性校验if (count < 0) throw new IllegalArgumentException("count < 0");// 构建内部类,Sync传入countthis.sync = new Sync(count);
}// AQS子类,Sync的有参构造
Sync(int count) {// 就是给AQS中的state赋值setState(count);
}

await方法

await方法就是判断当前CountDownLatch中的state是否为0,如果为0,直接正常执行后续任务。
如果不为0,以共享锁的方式,插入到AQS的双向链表,并且挂起线程。

// 一般主线程await的方法,阻塞主线程,等待state为0
public void await() throws InterruptedException {sync.acquireSharedInterruptibly(1);
}public final void acquireSharedInterruptibly(int arg)throws InterruptedException {// 判断线程是否中断,如果中断标记位是true,直接抛出异常if (Thread.interrupted())throw new InterruptedException();if (tryAcquireShared(arg) < 0)// 共享锁挂起的操作doAcquireSharedInterruptibly(arg);
}// tryAcquireShared在CountDownLatch中的实现
protected int tryAcquireShared(int acquires) {// 查看state是否为0,如果为0,返回1,不为0,返回-1return (getState() == 0) ? 1 : -1;
}private void doAcquireSharedInterruptibly(int arg)throws InterruptedException {// 封装当前先成为Node,属性为共享锁final Node node = addWaiter(Node.SHARED);boolean failed = true;try {for (;;) {final Node p = node.predecessor();if (p == head) {int r = tryAcquireShared(arg);if (r >= 0) {setHeadAndPropagate(node, r);p.next = null; // help GCfailed = false;return;}}// 在这,就需要挂起当前线程。if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())throw new InterruptedException();}} finally {if (failed)cancelAcquire(node);}
}

countDown方法

countDown方法本质就是对state - 1,如果state - 1后变为0,需要去AQS的链表中唤醒挂起的节点。

// countDown对计数器-1
public void countDown() {// 是-1。sync.releaseShared(1);
}// AQS提供的功能
public final boolean releaseShared(int arg) {// 对state - 1if (tryReleaseShared(arg)) {// state - 1后,变为0,执行doReleaseShareddoReleaseShared();return true;}return false;
}// CountDownLatch的tryReleaseShared实现
protected boolean tryReleaseShared(int releases) {// Decrement count; signal when transition to zero// 死循环是为了避免CAS并发问题for (;;) {// 获取stateint c = getState();// state已经为0,直接返回falseif (c == 0)return false;// 对获取到的state - 1int nextc = c-1;// 基于CAS的方式,将值赋值给stateif (compareAndSetState(c, nextc))// 赋值完,发现state为0了。此时可能会有线程在await方法处挂起,那边挂起,需要这边唤醒return nextc == 0;}
}// 如何唤醒在await方法处挂起的线程
private void doReleaseShared() {for (;;) {// 拿到headNode h = head;// head不为null,有值,并且head != tail,代表至少2个节点 // 一个虚拟的head,加上一个实质性的Nodeif (h != null && h != tail) {// 说明AQS队列中有节点int ws = h.waitStatus;// 如果head节点的状态为 -1.if (ws == Node.SIGNAL) {// 先对head节点将状态从-1,修改为0,避免重复唤醒的情况if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))continue;            // loop to recheck cases// 正常唤醒节点即可,先看head.next,能唤醒就唤醒,如果head.next有问题,从后往前找有效节点unparkSuccessor(h);}// 会在Semaphore中谈到这个位置else if (ws == 0 &&!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))continue;                // loop on failed CAS}// 会在Semaphore中谈到这个位置if (h == head)                   // loop if head changedbreak;}
}

CyclicBarrier应用&源码分析

CyclicBarrier介绍

从名字上来看CyclicBarrier,就是代表循环屏障。

Barrier屏障:让一个或多个线程达到一个屏障点,会被阻塞。屏障点会有一个数值,当达到一个线程阻塞在屏障点时,就会对屏障点的数值进行-1操作,当屏障点数值减为0时,屏障就会打开,唤醒所有阻塞在屏障点的线程。在释放屏障点之后,可以先执行一个任务,再让所有阻塞被唤醒的线程继续之后后续任务。

Cyclic循环:所有线程被释放后,屏障点的数值可以再次被重置。

CyclicBarrier一般被称为栅栏。

CyclicBarrier是一种同步机制,允许一组线程互相等待。现成的达到屏障点其实是基于await方法在屏障点阻塞。

CyclicBarrier并没有基于AQS实现,他是基于ReentrantLock锁的机制去实现了对屏障点–,以及线程挂起的操作。(CountDownLatch本身是基于AQS,对state进行release操作后,可以-1)

CyclicBarrier没来一个线程执行await,都会对屏障数值进行-1操作,每次-1后,立即查看数值是否为0,如果为0,直接唤醒所有的互相等待线程。

CyclicBarrier对比CountDownLatch区别

  • 底层实现不同。CyclicBarrier基于ReentrantLock做的。CountDownLatch直接基于AQS做的。
  • 应用场景不同。CountDownLatch的计数器只能使用一次。而CyclicBarrier在计数器达到0之后,可以重置计数器。CyclicBarrier可以实现相比CountDownLatch更复杂的业务,执行业务时出现了错误,可以重置CyclicBarrier计数器,再次执行一次。
  • CyclicBarrier还提供了很多其他的功能:
    • 可以获取到阻塞的现成有多少
    • 在线程互相等待时,如果有等待的线程中断,可以抛出异常,避免无限等待的问题。
  • CountDownLatch一般是让主线程等待,让子线程对计数器–。CyclicBarrier更多的让子线程 也一起计数和等待,等待的线程达到数值后,再统一唤醒

CyclicBarrier:多个线程互相等待,直到到达同一个同步点,再一次执行。

CyclicBarrier应用

出国旅游,导游小姐姐需要等待所有乘客都到位后,发送护照,签证等等文件,再一起出发。
比如Tom,Jack,Rose三个人组个团出门旅游。
在构建CyclicBarrier可以指定barrierAction,可以选择性指定,如果指定了,那么会在barrier归0后,优先执行barrierAction任务,然后再去唤醒所有阻塞挂起的线程,并行去处理后续任务。
所有互相等待的线程,可以指定等待时间,并且在等待的过程中,如果有线程中断,所有互相的等待的线程都会被唤醒。
如果在等待期间,有线程中断了,唤醒所有线程后,CyclicBarrier无法继续使用。
如果线程中断后,需要继续使用当前的CyclicBarrier,需要调用reset方法,让CyclicBarrier重置。
如果CyclicBarrier的屏障数值到达0之后,他默认会重置屏障数值,CyclicBarrier在没有线程中断时,是可以重复使用的。

public static void main(String[] args) throws InterruptedException {CyclicBarrier barrier = new CyclicBarrier(3, () -> {System.out.println("等到各位大佬都到位之后,分发护照和签证等内容!");});new Thread(() -> {System.out.println("Tom到位!!!");try {barrier.await();} catch (Exception e) {System.out.println("悲剧,人没到齐!");return;}System.out.println("Tom出发!!!");}).start();Thread.sleep(100);new Thread(() -> {System.out.println("Jack到位!!!");try {barrier.await();} catch (Exception e) {System.out.println("悲剧,人没到齐!");return;}System.out.println("Jack出发!!!");}).start();Thread.sleep(100);new Thread(() -> {System.out.println("Rose到位!!!");try {barrier.await();} catch (Exception e) {System.out.println("悲剧,人没到齐!");return;}System.out.println("Rose出发!!!");}).start();/* tom到位,jack到位,rose到位 导游发签证 tom出发,jack出发,rose出发*/
}

CyclicBarrier源码分析

分成两块内容去查看,首先查看CyclicBarrier的一些核心属性,然后再查看CyclicBarrier的核心方法。

CyclicBarrier的核心属性

public class CyclicBarrier {// 这个静态内部类是用来标记是否中断的private static class Generation {boolean broken = false;}/** CyclicBarrier是基于ReentrantLock实现的互斥操作,以及计数原子性操作 */private final ReentrantLock lock = new ReentrantLock();/** 基于当前的Condition实现线程的挂起和唤醒 */private final Condition trip = lock.newCondition();/** 记录有参构造传入的屏障数值,不会对这个数值做操作 */private final int parties;/** 当屏障数值达到0之后,优先执行当前任务 */private final Runnable barrierCommand;/** 初始化默认的Generation,用来标记线程中断情况 */private Generation generation = new Generation();/** 每来一个线程等待,就对count进行-- */private int count;
}

CyclicBarrier的有参构造

掌握构建CyclicBarrier之后,内部属性的情况

// 这个是CyclicBarrier的有参构造
// 在内部传入了parties,屏障点的数值
// 还传入了barrierAction,屏障点的数值达到0,优先执行barrierAction任务
public CyclicBarrier(int parties, Runnable barrierAction) {// 健壮性判if (parties <= 0) throw new IllegalArgumentException();// 当前类中的属性parties是保存屏障点数值的this.parties = parties;// 将parties赋值给属性count,每来一个线程,继续count做-1操作。this.count = parties;// 优先执行的任务this.barrierCommand = barrierAction;
}

CyclicBarrier中的await方法

在CyclicBarrier中,提供了2个await方法。

  • 第一个是无参的方式,线程要死等,直屏障点数值为0,或者有线程中断。
  • 第二个是有参方式,传入等待的时间,要么时间到位了,要不就是直屏障点数值为0,或者有线程中断。

无论是哪种await方法,核心都在于内部调用的dowait方法。
dowait方法主要包含了线程互相等待的逻辑,以及屏障点数值到达0之后的操作。

Semaphone应用&源码分析

Semaphore介绍

sync,ReentrantLock是互斥锁,保证一个资源同一时间只允许被一个线程访问。
Semaphore(信号量)保证1个或多个资源可以被指定数量的线程同时访问。
底层实现是基于AQS去做的。
Semaphore底层也是基于AQS的state属性做一个计数器的维护。state的值就代表当前共享资源的 个数。如果一个线程需要获取的1或多个资源,直接查看state的标识的资源个数是否足够,如果足够的,直接对state - 1拿到当前资源。如果资源不够,当前线程就需要挂起等待。知道持有资源的线程释放资源后,会归还给Semaphore中的state属性,挂起的线程就可以被唤醒。
Semaphore也分为公平和非公平的概念。
使用场景:连接池对象就可以基础信号量去实现管理。在一些流量控制上,也可以采用信号量去实现。再比如去迪士尼或者是环球影城,每天接受的人流量是固定的,指定一个具体的人流量,可能接 受10000人,每有一个人购票后,就对信号量进行–操作,如果信号量已经达到了0,或者是资源不足,此时就不能买票。

Semaphore应用

以上面环球影城每日人流量为例子去测试一下。

public static void main(String[] args) throws InterruptedException {// 今天环球影城还有人个人流量Semaphore semaphore = new Semaphore(10);new Thread(() -> {System.out.println("一家三口要去~~");try {semaphore.acquire(3);System.out.println("一家三口进去了~~~");Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();} finally {System.out.println("一家三口走了~~~");semaphore.release(3);}}).start();for (int i = 0; i < 7; i++) {int j = i;new Thread(() -> {System.out.println(j + "大哥来了。");try {semaphore.acquire();System.out.println(j + "大哥进去了~~~");Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();} finally {System.out.println(j + "大哥走了~~~");semaphore.release();}}).start();}Thread.sleep(10);System.out.println("main大哥来了。");if (semaphore.tryAcquire()) {System.out.println("main大哥进来了。");} else {System.out.println("资源不够,main大哥进来了。");}Thread.sleep(10000);System.out.println("main大哥又来了。");if (semaphore.tryAcquire()) {System.out.println("main大哥进来了。");semaphore.release();} else {System.out.println("资源不够,main大哥进来了。");}
}

其实Semaphore整体就是对构建Semaphore时,指定的资源数的获取和释放操作。
获取资源方式:

  • acquire():获取一个资源,没有资源就挂起等待,如果中断,直接抛异常
  • acquire(int):获取指定个数资源,资源不够,或者没有资源就挂起等待,如果中断,直接抛异常
  • tryAcquire():获取一个资源,没有资源返回false,有资源返回true tryAcquire(int):获取指定个数资源,没有资源返回false,有资源返回true
  • tryAcquire(time,unit):获取一个资源,如果没有资源,等待time.unit,如果还没有,就返回 false
  • tryAcquire(int,time,unit):获取指定个数资源,如果没有资源,等待time.unit,如果还没有,就返回false
  • acquireUninterruptibly():获取一个资源,没有资源就挂起等待,中断线程不结束,继续等
  • acquireUninterruptibly(int):获取指定个数资源,没有资源就挂起等待,中断线程不结束,继续等

归还资源方式:

  • release():归还一个资源
  • release(int):归还指定个数资源

Semaphore源码分析

先查看Semaphore的整体结构,然后基于获取资源,以及归还资源的方式去查看源码。

Semaphore的整体结构

Semaphore内部有3个静态内类。
首先是向上抽取的Sync。
其次还有两个Sync的子类NonFairSync以及FairSync两个静态内部类。
Sync内部主要提供了一些公共的方法,并且将有参构造传入的资源个数,直接基于AQS提供的setState方法设置了state属性。
NonFairSync以及FairSync区别就是tryAcquireShared方法的实现是不一样。

Semaphore的非公平的获取资源

在构建Semaphore的时候,如果只设置资源个数,默认情况下是非公平。
如果在构建Semaphore,传入了资源个数以及一个boolean时,可以选择非公平还是公平。

public Semaphore(int permits, boolean fair) {sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

从非公平的acquire方法入手

首先确认默认获取资源数是1个,并且acquire是允许中断线程时,抛出异常的。获取资源的方式, 就是直接用state - 需要的资源数,只要资源足够,就CAS的将state做修改。如果没有拿到锁资源, 就基于共享锁的方式去将当前线程挂起在AQS双向链表中。如果基于doAcquireSharedInterruptibly拿锁成功,会做一个事情。会执行setHeadAndPropagate方法。

// 信号量的获取资源方法(默认获取一个资源)
public void acquire() throws InterruptedException {// 跳转到了AQS中提供共享锁的方法sync.acquireSharedInterruptibly(1);
}// AQS提供的
public final void acquireSharedInterruptibly(int arg)throws InterruptedException {// 判断线程的中断标记位,如果已经中断,直接抛出异常if (Thread.interrupted())throw new InterruptedException();// 先看非公平的tryAcquireShared实现。// tryAcquireShared:// 返回小于0,代表获取资源失败,需要排队。// 返回大于等于0,代表获取资源成功,直接执行业务代码if (tryAcquireShared(arg) < 0)doAcquireSharedInterruptibly(arg);
}// 信号量的非公平获取资源方法
final int nonfairTryAcquireShared(int acquires) {for (;;) {// 获取state的数值,剩余的资源个数int available = getState();// 剩余的资源个数 - 需要的资源个数int remaining = available - acquires;// 如果-完后,资源个数小于0,直接返回这个负数if (remaining < 0 ||// 说明资源足够,基于CAS的方式,将state从原值,改为remainingcompareAndSetState(available, remaining))return remaining;}
}// 获取资源失败,资源不够,当前线程需要挂起等待
private void doAcquireSharedInterruptibly(int arg)throws InterruptedException {// 构建Node节点,线程和共享锁标记,并且到AQS双向链表中final Node node = addWaiter(Node.SHARED);boolean failed = true;try {for (;;) {// 拿到上一个节点final Node p = node.predecessor();// 如果是head.next,就抢一手if (p == head) {// 再次基于非公平的方式去获取一次资源int r = tryAcquireShared(arg);// 到这,说明拿到了锁资源if (r >= 0) {setHeadAndPropagate(node, r);p.next = null; // help GCfailed = false;return;}}// 如果上面没拿到,或者不是head的next节点,将前继节点的状态改为-1,并挂起当前线程if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())// 如果线程中断会抛出异常throw new InterruptedException();}} finally {if (failed)cancelAcquire(node);}
}

acquire()以及acquire(int)的方式,都是执行acquireSharedInterruptibly方法去尝试获取资源,区别只在于是否传入了需要获取的资源个数。
tryAcquire()以及tryAcquire(int因为这两种方法是直接执行tryAcquire,只使用非公平的实现,只有非公平的情况下,才有可能在有线程排队的时候获取到资源。

但是tryAcquire(int,time,unit)这种方法是正常走的AQS提供的acquire。因为这个tryAcquire可以排队一会,即便是公平锁也有可能拿到资源。这里的挂起和acquire挂起的区别仅仅是挂起的时间问题。

  • acquire是一直挂起直到线程中断,或者线程被唤醒。
  • tryAcquire(int,time,unit)是挂起一段时间,直到线程中断,要么线程被唤醒,要么阻塞时间到了。

还有acquireUninterruptibly()以及acquireUninterruptibly(int)只是在挂起线程后,不会因为线程 的中断而去抛出异常

Semaphore公平实现

公平与非公平只是差了一个方法的实现tryAcquireShared实现。
这个方法的实现中,如果是公平实现,需要先查看AQS中排队的情况。

// 信号量公平实现
protected int tryAcquireShared(int acquires) {for (;;) {// 公平实现在走下述逻辑前,先判断队列中排队的情况// 如果没有排队的节点,直接不走if逻辑// 如果有排队的节点,发现当前节点处在head.next位置,直接不走if逻辑if (hasQueuedPredecessors())return -1;// 下面这套逻辑和公平实现是一模一样的。int available = getState();int remaining = available - acquires;if (remaining < 0 ||compareAndSetState(available, remaining))return remaining;}
}

Semaphore释放资源

因为信号量从头到尾都是共享锁的实现…
释放资源操作,不区分公平和非公平。

// 信号量释放资源的方法入口
public void release() {sync.releaseShared(1);
}// 释放资源不分公平和非公平,都走AQS的releaseShared
public final boolean releaseShared(int arg) {// 优先查看tryReleaseShared,这个方法是信号量自行实现的。if (tryReleaseShared(arg)) {// 只要释放资源成功,执行doReleaseShared,唤醒AQS中排队的线程,去竞争Semaphore的资源doReleaseShared();return true;}return false;
}// 信号量实现的释放资源方法
protected final boolean tryReleaseShared(int releases) {for (;;) {// 拿到当前的stateint current = getState();// 将state + 归还的资源个数,新的state要被设置为nextint next = current + releases;// 如果归还后的资源个数,小于之前的资源数。// 避免出现归还资源后,导致next为负数,需要做健壮性判断if (next < current) // overflowthrow new Error("Maximum permit count exceeded");// CAS操作,保证原子性,只会有一个线程成功的就之前的state修改为nextif (compareAndSetState(current, next))return true;}
}

AQS中PROPAGATE节点

为了更好的了解PROPAGATE节点状态的意义,优先从JDK1.5去分析一下释放资源以及排队后获取资源的后置操作。

掌握JDK1.5-Semaphore执行流程图

首先查看4个线程获取信号量资源的情况
在这里插入图片描述
往下查看释放资源的过程会触发什么问题。
首先t1释放资源,做了进一步处理。
在这里插入图片描述
当线程3获取锁资源后,线程2再次释放资源,因为执行点问题,导致线程4无法被唤醒。

分析JDK1.8的变化

在这里插入图片描述

//====================================JDK1.5实现================================ 
public final boolean releaseShared(int arg) {if (tryReleaseShared(arg)) {Node h = head;if (h != null && h.waitStatus != 0)unparkSuccessor(h);return true;}return false;
}private void setHeadAndPropagate(Node node, int propagate) {setHead(node);if (propagate > 0 && node.waitStatus != 0) {Node s = node.next;if (s == null || s.isShared()) unparkSuccessor(node);}
}//====================================JDK1.8实现================================
public final boolean releaseShared(int arg) {if (tryReleaseShared(arg)) {doReleaseShared();return true;}return false;
}private void doReleaseShared() {for (;;) {// 拿到head节点Node h = head;// 判断AQS中有排队的Node节点if (h != null && h != tail) {// 拿到head节点的状态int ws = h.waitStatus;// 状态为-1if (ws == Node.SIGNAL) {// 将head节点的状态从-1,改为0if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))continue;            // loop to recheck cases// 唤醒后继节点unparkSuccessor(h);}// 发现head状态为0,将head状态从0改为-3,目的是为了往后面传播else if (ws == 0 &&!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))continue;                // loop on failed CAS}// 没有并发的时候。head节点没变化,正常完成释放排队的线程if (h == head)                   // loop if head changedbreak;}
}private void setHeadAndPropagate(Node node, int propagate) {// 拿到headNode h = head; // Record old head for check below// 将线程3的Node设置为新的headsetHead(node);// 如果propagate 大于0,代表还有剩余资源,直接唤醒后续节点,如果不满足,也需要继续往后判断看下是否需要传播// h == null:看成健壮性判断即可// 之前的head节点状态为负数,说明并发情况下,可能还有资源,需要继续向后唤醒Node// 如果当前新head节点的状态为负数,继续释放后续节点if (propagate > 0 || h == null || h.waitStatus < 0 ||(h = head) == null || h.waitStatus < 0) {// 唤醒当前节点的后继节点Node s = node.next;if (s == null || s.isShared())doReleaseShared();}
}

相关文章:

七、JUC并发工具

文章目录JUC并发工具CountDownLatch应用&源码分析CountDownLatch介绍CountDownLatch应用CountDownLatch源码分析有参构造await方法countDown方法CyclicBarrier应用&源码分析CyclicBarrier介绍CyclicBarrier应用CyclicBarrier源码分析CyclicBarrier的核心属性CyclicBarr…...

C++ string类(二)及深浅拷贝

一、string类方法使用举例1.迭代器迭代器本质&#xff1a;指针&#xff08;理解&#xff09;迭代器&#xff1a;正向迭代器&#xff1a; begin() | end() 反向迭代器&#xff1a; rbegin() | rend()2.find使用//找到s中某个字符 void TestString3() {string s("AAADEFNUIE…...

「TCG 规范解读」TCG 软件栈 TSS (上)

可信计算组织&#xff08;Ttrusted Computing Group,TCG&#xff09;是一个非盈利的工业标准组织&#xff0c;它的宗旨是加强在相异计算机平台上的计算环境的安全性。TCG于2003年春成立&#xff0c;并采纳了由可信计算平台联盟&#xff08;the Trusted Computing Platform Alli…...

(二)Markdown编辑器的使用效果 | 以CSDN自带MD编辑器为例

Markdown编辑器使用指南 &#xff08;一&#xff09;Markdown编辑器的使用示例 | 以CSDN自带MD编辑器为例&#xff08;二&#xff09;Markdown编辑器的使用效果 | 以CSDN自带MD编辑器为例 这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xf…...

WebSocket网络通信执行流程

目录WebSocket网络通信执行流程相关概念执行流程WebSocket网络通信执行流程 WebSocket协议&#xff1a;通过单个TCP连接在客户端和服务器之间建立全双工双向通信通道。 WebSocket 对象&#xff1a;提供了用于创建和管理 WebSocket 连接&#xff0c;以及可以通过该连接发送和接…...

【Shell学习笔记】4.Shell 基本运算符

前言 本章介绍Shell的基本运算符。 Shell 基本运算符 Shell 和其他编程语言一样&#xff0c;支持多种运算符&#xff0c;包括&#xff1a; 算数运算符关系运算符布尔运算符字符串运算符文件测试运算符 原生bash不支持简单的数学运算&#xff0c;但是可以通过其他命令来实现…...

无代码资讯 | 《低代码开发平台能力要求》发布;CADP列入Gartner《2022-2024 中型企业技术采用路线图》

栏目导读&#xff1a;无代码资讯栏目从全球视角出发&#xff0c;带您了解无代码相关最新资讯‍。TOP3 大事件1、《低代码开发平台能力要求》团体标准正式发布近日&#xff0c;中国电子工业标准化协会发布公告&#xff08;中电标【2022】037 号&#xff09;&#xff0c;由中国电…...

智能家居Homekit系列一智能插座

WiFi智能插座对于新手接触智能家居产品更加友好&#xff0c;不需要额外购买网关设备 很多智能小配件也给我们得生活带来极大的便捷&#xff0c;智能插座就是其中之一&#xff0c;比如外出忘记关空调&#xff0c;可以拿起手机远程关闭。 简单说就是&#xff1a;插座可以连接wi…...

React(三):脚手架、组件化、生命周期、父子组件通信、插槽

React&#xff08;三&#xff09;一、脚手架安装和创建1.安装脚手架2.创建脚手架3.看看脚手架目录4.运行脚手架二、脚手架下从0开始写代码三、组件化1.类组件2.函数组件四、React的生命周期1.认识生命周期2.图解生命周期&#xff08;1&#xff09;Constructor&#xff08;2&…...

2023年电子竞技行业报告

第一章 行业概况 电子竞技也被称为电竞或eSports&#xff0c;是一种电子游戏的竞技活动&#xff0c;玩家在这里与其他人或团队对战&#xff0c;通常是在网络上或特定场地上进行。 电子竞技行业的发展与互联网和计算机技术的进步密不可分&#xff0c;同时还受到游戏开发商、赞…...

小朋友就餐-课后程序(JAVA基础案例教程-黑马程序员编著-第八章-课后作业)

【案例8-5】 小朋友就餐问题 【案例介绍】 1.任务描述 一圆桌前坐着5位小朋友&#xff0c;两个人中间有一只筷子&#xff0c;桌子中央有面条。小朋友边吃边玩&#xff0c;当饿了的时候拿起左右两只筷子吃饭&#xff0c;必须拿到两只筷子才能吃饭。但是&#xff0c;小朋友在吃…...

大数据|Hadoop系统

目录 &#x1f4da;Hadoop介绍 &#x1f4da;Hadoop优点 &#x1f4da;Hadoop的体系结构 &#x1f430;HDFS的体系结构 &#x1f430;MapReduce的体系结构 &#x1f430;HDFS和MapReduce的协同作用 &#x1f4da;Hadoop与分布式开发 &#x1f430;MapReduce计算模型 &a…...

2.递归算法

递归算法的两个特点&#xff08;很重要&#xff09;调用自身要有结束条件void func1(int x) {printf("%d\n", x);func1(x - 1); }func1会一直死循环&#xff0c;没有使其结束的条件&#xff0c;所以不是递归void func2(int x) {if (x > 0){printf("%d\n"…...

MySQL---触发器

MySQL—触发器 ​ 将两个关联的操作步骤写到程序里面&#xff0c;并且要用事务包裹起来&#xff0c;确保两个操作称为一个原子操作&#xff0c;要么全部执行&#xff0c;要么全部不执行 ​ 创建一个触发器&#xff0c;让商品信息数据的插入操作自动触发库存数据的插入操作 ​…...

PXC高可用集群(MySQL)

1. PXC集群概述 1.1. PXC介绍 Percona XtraDB Cluster&#xff08;简称PXC&#xff09; 是基于Galera的MySQL高可用集群解决方案Galera Cluster是Codership公司开发的一套免费开源的高可用方案PXC集群主要由两部分组成&#xff1a;Percona Server with XtraDB&#xff08;数据…...

pytorch-把线性回归实现一下。原理到实现,python到pytorch

线性回归 线性回归输出是一个连续值&#xff0c;因此适用于回归问题。回归问题在实际中很常见&#xff0c;如预测房屋价格、气温、销售额等连续值的问题。 与回归问题不同&#xff0c;分类问题中模型的最终输出是一个离散值。所说的图像分类、垃圾邮件识别、疾病检测等输出为离…...

js中判断数组的方式有哪些?

js中判断数组的方式有哪些&#xff1f;1.通过Object.prototype.toString.call来判断2.通过instanceof来判断3.通过constructor来判断4.通过原型链来判断5.通过ES6.Array.isAaary()来判断6.通过Array.prototype.isPrototypeOf来判断1.通过Object.prototype.toString.call来判断 …...

【2023unity游戏制作-mango的冒险】-5.攻击系统的简单实现

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 收录于专栏&#xff1a;unity游戏制作 ⭐攻击系统的简单实现⭐ 文章目录⭐攻击系统的简单实现⭐&#x1f468;‍&#x1…...

SpringMVC 面试题

1、什么是SpringMVC&#xff1f; SpringMVC是一个基于Java的实现了MVC设计模式的“请求驱动型”的轻量级WEB框架&#xff0c;通过把model&#xff0c;view&#xff0c;controller 分离&#xff0c;将web层进行职责的解耦&#xff0c;把复杂的web应用分成逻辑清晰的几个部分&am…...

布局三八女王节,巧借小红书数据分析工具成功引爆618

对于小红书“她”经济来说&#xff0c;没有比三八节更好的阵地了。伴随三八女王节逐渐临近&#xff0c;各大品牌蓄势待发&#xff0c;这场开春后第一个S级大促活动&#xff0c;看看品牌方们可以做什么&#xff1f; 洞察流量&#xff0c;把握节点营销时机 搜索小红书2023年的三…...

RISCV学习(1)基本模型认识

笔者来聊聊ARM的函数的调用规则 1、ARM函数调用规则介绍 首先介绍几个术语&#xff0c; AAPCS&#xff1a;Procedure Call Standard for the ARM ArchitectureAPCS&#xff1a;ARM Procedure Call StandardTPCS&#xff1a;Thumb Procedure Call StandardATPCS&#xff1a;AR…...

【java代码审计】命令注入

1 成因 开发者在某种开发需求时&#xff0c;需要引入对系统本地命令的支持来完成某些特定的功能&#xff0c;此时若未对用户的输入做严格的过滤&#xff0c;就可能发生命令注入。 2 造成命令注入的类或方法 Runtime类&#xff1a;提供调用系统命令的功能 ①Runtime.getRuntim…...

速锐得适配北汽EX系列电动汽车CAN总线应用于公务分时租赁

过去的几年&#xff0c;我们看到整个分时租赁业务出现断崖式下跌&#xff0c;这是我们看到这种市场情况&#xff0c;是必然&#xff0c;也是出乎意料。原本很多融资后的出行公司、大牌的出行服务商的分时租赁业务&#xff0c;受各种影响不得不转型成其他出行服务。例如&#xf…...

已解决ERROR: Failed building wheel for opencv-python-headless

已解决ERROR: Failed building wheel for opencv-python-headless Failed to build opencv-python-headless ERROR: Could not build wheels for opencv-python-headless, which is required to install pyproject.toml-based projects报错信息亲测有效 文章目录报错问题报错翻…...

每日获取安全资讯的网站,国内外共120个

国内 FreeBuf&#xff08;https://www.freebuf.com/&#xff09; 安全客&#xff08;https://www.anquanke.com/&#xff09; 雷锋网安全&#xff08;https://www.leiphone.com/category/security&#xff09; 先知社区&#xff08;https://xz.aliyun.com/&#xff09; CSDN安全…...

HUN工训中心:开关电路和按键信号抖动

工训中心的牛马实验 1.实验目的&#xff1a; 1) 认识开关电路&#xff0c;掌握按键状态判别、开关电路中逻辑电平测量、逻辑值和逻辑函数电路。 2) 掌握按键信号抖动简单处理方法。 3) 实现按键计数电路。 2.实验资源&#xff1a; HBE硬件基础电路实验箱、示波器、万用表…...

WordPress 主题 SEO 标题相关函数和过滤器教程wp_get_document_title()

WordPress 4.4.0 版本开始&#xff0c;加入了 wp_get_document_title(); 这个函数&#xff0c;而 wp_title(); 已经 deprecated 不推荐使用。因此&#xff0c;如果想要启用 WordPress 主题标题功能&#xff0c;在不安装 WordPress SEO 插件的情况下&#xff0c;可以使用以下代码…...

Qt 事件机制

【1】事件 事件是可以被控件识别的操作。如按下确定按钮、选择某个单选按钮或复选框。 每种控件有自己可识别的事件&#xff0c;如窗体的加载、单击、双击等事件&#xff0c;编辑框&#xff08;文本框&#xff09;的文本改变事件等等。 事件就是用户对窗口上各种组件的操作。…...

【Python】Numpy--np.linalg.eig()求对称矩阵的特征值和特征向量

【Python】Numpy–np.linalg.eig()求对称矩阵的特征值和特征向量 文章目录【Python】Numpy--np.linalg.eig()求对称矩阵的特征值和特征向量1. 介绍2. API3. 代码示例1. 介绍 特征分解&#xff08;Eigendecomposition&#xff09;&#xff0c;又称谱分解&#xff08;Spectral d…...

医疗床头卡(WIFI方案)

一、产品特性 7.5寸墨水屏显示WIFI无线通信&#xff0c;极简部署&#xff0c;远程控制按键及高亮LED指示灯指示800*480点阵屏幕锂电池供电&#xff0c;支持USB充电DIY界面支持文本/条码/二维码/图片超低功耗/超长寿命&#xff0c;一次充电可用一年基于现有Wifi环境&#xff0c…...

[YOLO] yolo博客笔记汇总(自用

pip下载速度太慢&#xff0c;国内镜像&#xff1a; 国内镜像解决pip下载太慢https://blog.csdn.net/weixin_51995286/article/details/113972534​​​​​​​ YOLO v2和V3 关于设置生成anchorbox&#xff0c;Boundingbox边框回归的过程详细解读 YOLO v2和V3 关于设置生成an…...

Linux 常用 API 函数

文章目录1. 系统调用与库函数1.1 什么是系统调用1.2 系统调用的实现1.3 系统调用和库函数的区别2. 虚拟内存空间3. 错误处理函数4. C 库中 IO 函数工作流程5. 文件描述符6. 常用文件 IO 函数6.1 open 函数6.2 close 函数6.3 write 函数6.4 read 函数6.5 lseek 函数7. 文件操作相…...

【转载】bootstrap自定义样式-bootstrap侧边导航栏的实现

bootstrap自带的响应式导航栏是向下滑动的&#xff0c;但是有时满足不了个性化的需求: 侧滑栏使用定位fixed 使用bootstrap响应式使用工具类 visible-sm visible-xs hidden-xs hidden-sm等对不同屏幕适配 侧滑栏的侧滑效果不使用jquery方法来实现&#xff0c;使用的是css3 tr…...

奇瑞x华为纯电智选车来了,新版ADS成本将大幅下降

作者 | 德新 编辑 | 于婷HiEV获悉&#xff0c;问界M5将在4月迎来搭载高阶辅助驾驶的新款&#xff0c;而M9将在今年秋天发布。 奇瑞一侧&#xff0c;华为将与奇瑞首先推出纯电轿车&#xff0c;代号EH3。新车将在奇瑞位于芜湖江北新区的智能网联超级二工厂组装下线。目前超级二工…...

机器学习的特征归一化Normalization

为什么需要做归一化&#xff1f; 为了消除数据特征之间的量纲影响&#xff0c;就需要对特征进行归一化处理&#xff0c;使得不同指标之间具有可比性。对特征归一化可以将所有特征都统一到一个大致相同的数值区间内。 为了后⾯数据处理的⽅便&#xff0c;归⼀化可以避免⼀些不…...

程序员看过都说好的资源网站,看看你都用过哪些?

程序员必备的相关资源网站一.图片专区1.表情包&#xff08;1&#xff09;发表情&#xff08;2&#xff09;逗比拯救世界&#xff08;3&#xff09;搞怪图片生成&#xff08;4&#xff09;哇咔工具2.图标库&#xff08;1&#xff09;Font Awesome&#xff08;2&#xff09;iconf…...

Win11的两个实用技巧系列之设置系统还原点的方法、安全启动状态开启方法

Win11如何设置系统还原点?Win11设置系统还原点的方法很多用户下载安装win11后应该如何创建还原点呢&#xff1f;现在我通过这篇文章给大家介绍一下Win11如何设置系统还原点&#xff1f;在Windows系统中有一个系统还原功能可以帮助我们在电脑出现问题的时候还原到设置的时间上&…...

【Linux】项目的自动化构建-make/makefile

&#x1f4a3;1.背景会不会写makefile&#xff0c;从一个侧面说明了一个人是否具备完成大型工程的能力 一个工程中的源文件不计数&#xff0c;其按类型、功能、模块分别放在若干个目录中&#xff0c;makefile定义了一系列的 规则来指定&#xff0c;哪些文件需要先编译&#xff…...

【Redis学习2】Redis常用数据结构与应用场景

Redis常用数据结构与应用场景 redis中存储数据是以key-value键值对的方式去存储的&#xff0c;其中key为string字符类型&#xff0c;value的数据类型可以是string(字符串)、list(列表)、hash(字典)、set(集合) 、 zset(有序集合)。 这5种数据类型在开发中可以应对大部分场景的…...

踩了大坑:https 证书访问错乱

文章目录一、问题排查及解决问题一&#xff1a;证书加载错乱问题二&#xff1a;DNS 解析污染问题问题三&#xff1a;浏览器校验问题二、终极解决方法2.1 可外网访问域名2.2 只能内网访问域名2.3 内网自动化配置2.4 错误解决一、问题排查及解决 今天遇到这样一个问题&#xff0…...

大数据技术之Hive(四)分区表和分桶表、文件格式和压缩

一、分区表和分桶表1.1 分区表partitionhive中的分区就是把一张大表的数据按照业务需要分散的存储到多个目录&#xff0c;每个目录就称为该表的一个分区。在查询时通过where子句中的表达式选择式选择查询所需要的分区&#xff0c;这样的查询效率辉提高很多。1.1.1 分区表基本语…...

环形缓冲区(c语言)

1、概念介绍 在我们需要处理大量数据的时候&#xff0c;不能存储所有的数据&#xff0c;只能先处理先来的&#xff0c;然后将这个数据释放&#xff0c;再去处理下一个数据。 如果在一个线性的缓冲区中&#xff0c;那些已经被处理的数据的内存就会被浪费掉。因为后面的数据只能…...

创建自助服务知识库的指南

在SaaS领域&#xff0c;自助文档是你可以在客户登录你的网站时为他们提供的最灵活的帮助方式&#xff0c;简单来说&#xff0c;一个自助知识库是一个可以帮助许多客户的文档&#xff0c;拥有出色的自助服务知识库&#xff0c;放在官网或者醒目的地方&#xff0c;借助自助服务知…...

分层测试(1)分层测试是什么?【必备】

1. 什么是分层测试&#xff1f; 分层测试是通过对质量问题分类、分层来保证整体系统质量的测试体系。 模块内通过接口测试保证模块质量&#xff0c;多模块之间通过集成测试保证通信路径和模块间交互质量&#xff0c;整体系统通过端到端用例对核心业务场景进行验证&#xff0c…...

开源ZYNQ AD9361软件无线电平台

&#xff08;1&#xff09; XC7Z020-CLG400 &#xff08;2&#xff09; AD9363 &#xff08;3&#xff09; 单发单收&#xff0c;工作频率400MHz-2.7GHz &#xff08;4&#xff09; 发射带PA&#xff0c;最大输出功率约20dbm &#xff08;5&#xff09; 接收带LNA&#xff0c;低…...

第四阶段-12关于Spring Security框架,RBAC,密码加密原则

关于csmall-passport项目 此项目主要用于实现“管理员”账号的后台管理功能&#xff0c;主要实现&#xff1a; 管理员登录添加管理员删除管理员显示管理员列表启用 / 禁用管理员 关于RBAC RBAC&#xff1a;Role-Based Access Control&#xff0c;基于角色的访问控制 在涉及…...

JPA——Date拓展之Calendar

Java Calendar 是时间操作类,Calendar 抽象类定义了足够的方法&#xff0c;在某一特定的瞬间或日历上&#xff0c;提供年、月、日、小时之间的转换提供方法 一、获取具体时间信息 1. 当前时间 获取此刻时间的年月日时分秒 Calendar calendar Calendar.getInstance(); int …...

一文吃透 Spring 中的 AOP 编程

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…...

Apple主推的智能家居是什么、怎么用?一篇文章带你从零完全入门 HomeKit

如果你对智能家居有所了解&#xff0c;那应该或多或少听人聊起过 HomeKit。由 Apple 开发并主推的的 HomeKit 既因为产品选择少、价格高而难以成为主流&#xff0c;又因其独特的优秀体验和「出身名门」而成为智能家居领域的焦点。HomeKit 究竟是什么&#xff1f;能做什么&#…...

SpringCloud系列知识快速复习 -- part 1(SpringCloud基础知识,Docker,RabbitMQ)

SpringCloud知识快速复习SpringCloud基础知识微服务特点SpringCloud常用组件服务拆分和提供者与消费者概念Eureka注册中心原理Ribbon负载均衡原理负载均衡策略饥饿加载Nacos注册中心服务分级存储模型权重配置环境隔离Nacos与Eureka的区别Nacos配置管理拉取配置流程配置热更新配…...