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

JUC并发—7.AQS源码分析三

大纲

1.等待多线程完成的CountDownLatch介绍

2.CountDownLatch.await()方法源码

3.CountDownLatch.coutDown()方法源码

4.CountDownLatch总结

5.控制并发线程数的Semaphore介绍

6.Semaphore的令牌获取过程

7.Semaphore的令牌释放过程

8.同步屏障CyclicBarrier介绍

9.CyclicBarrier的await()方法源码

10.使用CountDownLatch等待注册的完成

11.使用CyclicBarrier将工作任务多线程分而治之

12.使用CyclicBarrier聚合服务接口的返回结果

13.使用Semaphore等待指定数量线程完成任务

volatile、synchronized、CAS、AQS、读写锁、锁优化和锁故障、并发集合、线程池、同步组件

1.等待多线程完成的CountDownLatch

(1)CountDownLatch的简介

(2)CountDownLatch的应用

(3)CountDownLatch的例子

(1)CountDownLatch的简介

CountDownLatch允许一个或多个线程等待其他线程完成操作。CountDownLatch提供了两个核心方法,分别是await()方法和countDown()方法。CountDownLatch.await()方法让调用线程进行阻塞进入等待状态,CountDownLatch.countDown()方法用于对计数器进行递减。

CountDownLatch在构造时需要传入一个正整数作为计数器初始值。线程每调用一次countDown()方法,都会对该计数器减一。当计数器为0时,会唤醒所有执行await()方法时被阻塞的线程。

(2)CountDownLatch的应用

应用一:

使用多线程去解析一个Excel里多个sheet的数据,每个线程解析一个sheet里的数据,等所有sheet解析完再提示处理完成。此时便可以使用CountDownLatch来实现,当然可以使用Thread.join()方法。

注意:Thread.join()方法是基于wait()和notify()来实现的。在main线程里开启一个线程A,main线程如果执行了线程A的join()方法,那么就会导致main线程被阻塞,main线程会等待线程A执行完毕才会继续往下执行。

应用二:

微服务注册中心的register-client,为了在注册线程执行成功后,才发送心跳。可以使用CountDownLatch,当然也可以使用Thread.join()方法。

应用三:

可以通过CountDownLatch实现类似并发的效果。把CountDownLatch的计数器设置为1,然后让1000个线程调用await()方法。当1000个线程初始化完成后,在main线程调用countDown()让计数器归零。这样这1000个线程就会在一个for()循环中,依次被唤醒。

(3)CountDownLatch的例子

public class CountDownLatchDemo {public static void main(String[] args) throws Exception {final CountDownLatch latch = new CountDownLatch(2);new Thread() {public void run() {try {Thread.sleep(1000);System.out.println("线程1开始执行,休眠2秒...");Thread.sleep(1000);System.out.println("线程1准备执行countDown操作...");latch.countDown();System.out.println("线程1完成执行countDown操作...");} catch (Exception e) {e.printStackTrace();}}}.start();new Thread() {public void run() {try {Thread.sleep(1000);System.out.println("线程2开始执行,休眠2秒...");Thread.sleep(1000);System.out.println("线程2准备执行countDown操作...");latch.countDown();System.out.println("线程2完成执行countDown操作...");} catch (Exception e) {e.printStackTrace();}}}.start();System.out.println("main线程准备执行countDownLatch的await操作,将会同步阻塞等待...");latch.await();System.out.println("所有线程都完成countDown操作,结束同步阻塞等待...");}
}

2.CountDownLatch.await()方法源码

(1)CountDownLatch.await()方法的阻塞流程

(2)CountDownLatch.await()方法的唤醒流程

(3)CountDownLatch.await()方法的阻塞总结

(1)CountDownLatch.await()方法的阻塞流程

CountDownLatch是基于AQS中的共享锁来实现的。从CountDownLatch的构造方法可知,CountDownLatch的count就是AQS的state。

调用CountDownLatch的await()方法时,会先调用AQS的acquireSharedInterruptibly()模版方法,然后会调用CountDownLatch的内部类Sync实现的tryAcquireShared()方法。tryAcquireShared()方法会判断state的值是否为0,如果为0,才返回1,否则返回-1。

当调用CountDownLatch内部类Sync的tryAcquireShared()方法获得的返回值是-1时,才会调用AQS的doAcquireSharedInterruptibly()方法,将当前线程封装成Node结点加入等待队列,然后挂起当前线程进行阻塞。

//A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
public class CountDownLatch {private final Sync sync;public CountDownLatch(int count) {if (count < 0) {throw new IllegalArgumentException("count < 0");}this.sync = new Sync(count);}//Synchronization control For CountDownLatch.//Uses AQS state to represent count.private static final class Sync extends AbstractQueuedSynchronizer {Sync(int count) {setState(count);}int getCount() {return getState();}protected int tryAcquireShared(int acquires) {return (getState() == 0) ? 1 : -1;}protected boolean tryReleaseShared(int releases) {//Decrement count; signal when transition to zerofor (;;) {int c = getState();if (c == 0) {return false;}int nextc = c-1;if (compareAndSetState(c, nextc)) {return nextc == 0;}}}}//Causes the current thread to wait until the latch has counted down to zero, //unless the thread is Thread#interrupt interrupted.public void await() throws InterruptedException {//执行AQS的acquireSharedInterruptibly()方法sync.acquireSharedInterruptibly(1);}...
}public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {...//Acquires in shared mode, aborting if interrupted.//Implemented by first checking interrupt status, then invoking at least once #tryAcquireShared, returning on success.//Otherwise the thread is queued, possibly repeatedly blocking and unblocking,//invoking #tryAcquireShared until success or the thread is interrupted.public final void acquireSharedInterruptibly(int arg) throws InterruptedException {if (Thread.interrupted()) {throw new InterruptedException();}//执行CountDownLatch的内部类Sync实现的tryAcquireShared()方法,抢占共享锁if (tryAcquireShared(arg) < 0) {//执行AQS的doAcquireSharedInterruptibly()方法doAcquireSharedInterruptibly(arg);}}//Acquires in shared interruptible mode.private void doAcquireSharedInterruptibly(int arg) throws InterruptedException {final Node node = addWaiter(Node.SHARED);//封装当前线程为Shared类型的Node结点boolean failed = true;try {//第一次循环r = -1,所以会执行AQS的shouldParkAfterFailedAcquire()方法//将node结点的有效前驱结点的状态设置为SIGNALfor (;;) {final Node p = node.predecessor();//node结点的前驱结点if (p == head) {int r = tryAcquireShared(arg);if (r >= 0) {setHeadAndPropagate(node, r);p.next = null; // help GCfailed = false;return;}}//执行shouldParkAfterFailedAcquire()方法设置node结点的前驱结点的状态为SIGNAL//执行parkAndCheckInterrupt()方法挂起当前线程if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) {throw new InterruptedException();}}} finally {if (failed) {cancelAcquire(node);}}}//Checks and updates status for a node that failed to acquire.//Returns true if thread should block. This is the main signal control in all acquire loops.private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {int ws = pred.waitStatus;if (ws == Node.SIGNAL) {//This node has already set status asking a release to signal it, so it can safely park.return true;}if (ws > 0) {//Predecessor was cancelled. Skip over predecessors and indicate retry.do {node.prev = pred = pred.prev;} while (pred.waitStatus > 0);pred.next = node;} else {//waitStatus must be 0 or PROPAGATE.  //Indicate that we need a signal, but don't park yet.  //Caller will need to retry to make sure it cannot acquire before parking.compareAndSetWaitStatus(pred, ws, Node.SIGNAL);}return false;}//设置头结点和唤醒后续线程//Sets head of queue, and checks if successor may be waiting in shared mode, //if so propagating if either propagate > 0 or PROPAGATE status was set.private void setHeadAndPropagate(Node node, int propagate) {Node h = head;setHead(node);//将node结点设置为头结点if (propagate > 0 || h == null || h.waitStatus < 0 || (h = head) == null || h.waitStatus < 0) {Node s = node.next;if (s == null || s.isShared()) {doReleaseShared();}}}private void setHead(Node node) {head = node;node.thread = null;node.prev = null;}...
}

(2)CountDownLatch.await()方法的唤醒流程

调用await()方法时,首先会将当前线程封装成Node结点并添加到等待队列中,然后在执行第一次for循环时会设置该Node结点的前驱结点状态为SIGNAL,接着在执行第二次for循环时才会将当前线程进行挂起阻塞。

当该线程后续被唤醒时,该线程又会进入下一次for循环。如果该线程对应的node结点的前驱结点是等待队列的头结点且state值已为0,那么就执行AQS的setHeadAndPropagate()方法设置头结点 + 唤醒后续线程。

其中setHeadAndPropagate()方法有两个工作(设置头结点 + 唤醒传递):

工作一:设置当前被唤醒线程对应的结点为头结点

工作二:当满足如下这两个条件的时候需要调用doReleaseShared()方法唤醒后续的线程

条件一:propagate > 0,表示当前是共享锁,需要进行唤醒传递

条件二:s.isShared()判断当前结点为共享模式

CountDownLatch的实现中会在以下两个场景调用doReleaseShared()方法:

场景一:state为1时调用的countDown()方法会调用doReleaseShared()方法

场景二:当阻塞的线程被唤醒时,会调用setHeadAndPropagate()方法,进而调用doReleaseShared()方法,这样可以提升唤醒共享结点的速度

(3)CountDownLatch.await()方法的阻塞总结

只要state != 0,就会进行如下处理:

一.将当前线程封装成一个Node结点,然后添加到AQS的等待队列中

二.调用LockSupport.park()方法,挂起当前线程

3.CountDownLatch.coutDown()方法源码

(1)CountDownLatch.coutDown()的唤醒流程

(2)CountDownLatch.tryReleaseShared()

(3)AQS的doReleaseShared()方法

(1)CountDownLatch.coutDown()的唤醒流程

调用CountDownLatch的countDown()方法时,会先调用AQS的releaseShared()模版方法,然后会执行CountDownLatch的内部类Sync实现的tryReleaseShared()方法。

如果tryReleaseShared()方法返回true,则执行AQS的doReleaseShared()方法,通过AQS的doReleaseShared()方法唤醒共享锁模式下的等待队列中的线程。

//A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
public class CountDownLatch {private final Sync sync;public CountDownLatch(int count) {if (count < 0) {throw new IllegalArgumentException("count < 0");}this.sync = new Sync(count);}//Synchronization control For CountDownLatch.//Uses AQS state to represent count.private static final class Sync extends AbstractQueuedSynchronizer {Sync(int count) {setState(count);}int getCount() {return getState();}protected int tryAcquireShared(int acquires) {return (getState() == 0) ? 1 : -1;}protected boolean tryReleaseShared(int releases) {//Decrement count; signal when transition to zerofor (;;) {int c = getState();if (c == 0) {return false;}int nextc = c-1;if (compareAndSetState(c, nextc)) {return nextc == 0;}}}}//Decrements the count of the latch, releasing all waiting threads if the count reaches zero.public void countDown() {//执行AQS的releaseShared()方法sync.releaseShared(1);}...
}public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {...//Releases in shared mode.  //Implemented by unblocking one or more threads if #tryReleaseShared returns true.public final boolean releaseShared(int arg) {//执行CountDownLatch的内部类Sync实现的tryReleaseShared()方法,释放共享锁if (tryReleaseShared(arg)) {//执行AQS的doReleaseShared()方法doReleaseShared();return true;}return false;}//Release action for shared mode -- signals successor and ensures propagation. //Note: For exclusive mode, release just amounts to calling unparkSuccessor of head if it needs signal.private void doReleaseShared() {for (;;) {//每次循环时头结点都会发生变化//因为调用unparkSuccessor()方法会唤醒doAcquireSharedInterruptibly()方法中阻塞的线程//然后阻塞的线程会在执行setHeadAndPropagate()方法时通过setHead()修改头结点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}//唤醒头结点的后继结点//唤醒的线程会在doAcquireSharedInterruptibly()方法中执行setHeadAndPropagate()方法修改头结点unparkSuccessor(h);} else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) {//如果ws = 0表示初始状态,则修改结点为PROPAGATE状态continue;//loop on failed CAS}}if (h == head) {//判断头结点是否有变化break;//loop if head changed}}}//Wakes up node's successor, if one exists.private void unparkSuccessor(Node node) {int ws = node.waitStatus;if (ws < 0) {compareAndSetWaitStatus(node, ws, 0);}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);}}...
}

(2)CountDownLatch.tryReleaseShared()

从tryReleaseShared()方法可知:每次countDown()其实就是把AQS的state值减1,然后通过CAS更新state值。如果CAS设置成功,那么就判断当前state值是否为0。如果是0那么就返回true,如果不是0那么就返回false。返回true的时候会调用AQS的doReleaseShared()方法,唤醒等待队列中的线程。

(3)AQS的doReleaseShared()方法

该方法要从AQS的等待队列中唤醒头结点的后继结点,需要满足:

条件一:等待队列中要存在挂起线程的结点(h != null && h != tail)

条件二:等待队列的头结点的状态正常(h.waitStatus = Node.SIGNAL)

在共享锁模式下,state为0时需要通过唤醒传递把所有挂起的线程都唤醒。首先doReleaseShared()方法会通过for(;;)进行自旋操作,每次循环都会通过Node h = head来获取等待队列中最新的头结点,然后通过if (h == head)来判断等待队列中的头结点是否发生变化。如果没有变化,则退出自旋。

注意:在共享锁模式下,被unparkSuccessor()唤醒的等待队列中的线程,会继续在在doAcquireSharedInterruptibly()方法中,执行setHeadAndPropagate()方法修改头结点,从而实现唤醒传递。

4.CountDownLatch总结

假设有两个线程A和B,分别调用了CountDownLatch的await()方法,此时state所表示的计数器不为0。所以线程A和B会被封装成SHARED类型的结点,并添加到AQS的等待队列中。

当线程C调用CountDownLatch的coutDown()方法后,如果state被递减到0,那么就会调用doReleaseShared()方法唤醒等待队列中的线程。然后被唤醒的线程会继续调用setHeadAndPropagate()方法实现唤醒传递,从而继续在doReleaseShared()方法中唤醒所有在等待队列中的被阻塞的线程。

5.控制并发线程数的Semaphore介绍

(1)Semaphore的作用

(2)Semaphore的方法

(3)Semaphore原理分析

(1)Semaphore的作用

Semaphore信号量用来控制同时访问特定资源的线程数量,有两核心方法。

方法一:acquire()方法,获取一个令牌

方法二:release()方法,释放一个令牌

多个线程访问某限制访问流量的资源时,可先调用acquire()获取访问令牌。如果能够正常获得,则表示允许访问。如果令牌不够,则会阻塞当前线程。当某个获得令牌的线程通过release()方法释放一个令牌后,被阻塞在acquire()方法的线程就有机会获得这个释放的令牌。

public class SemaphoreDemo {public static void main(String[] args) throws InterruptedException {Semaphore semaphore = new Semaphore(10, true);//初始化10个资源,使用公平锁 semaphore.acquire();//每次获取一个资源,如果获取不到,线程就会阻塞semaphore.release();//释放一个资源}
}

(2)Semaphore的方法

Semaphore实际上并没有一个真实的令牌发给线程,Semaphore只是对一个可分配数量进行计数维护,或者说进行许可证管理。Semaphore可以在公共资源有限的场景下实现流量控制,如数据库连接。

一.Semaphore(permits, fair):permits表示令牌数,fair表示公平性
二.acquire(permits):获取指定数量的令牌,如果数量不足则阻塞当前线程
三.tryAcquire(permits):尝试获取指定数量的令牌,此过程是非阻塞的,成功返回true,失败返回false 
四.release(permits):释放指定数量的令牌
五.drainPermits():当前线程获得剩下的所有令牌
六.hasQueuedThread():判断当前Semaphore实例上是否存在等待令牌的线程

(3)Semaphore原理分析

Semaphore也是基于AQS中的共享锁来实现的。在创建Semaphore实例时传递的参数permits,其实就是AQS中的state属性。每次调用Semaphore的acquire()方法,都会对state值进行递减。

所以从根本上说,Semaphore是通过重写AQS的两个方法来实现的:

方法一:tryAcquireShared(),抢占共享锁

方法二:tryReleaseShared(),释放共享锁

public class Semaphore implements java.io.Serializable {private final Sync sync;//Creates a Semaphore with the given number of permits and nonfair fairness setting.public Semaphore(int permits) {sync = new NonfairSync(permits);}static final class NonfairSync extends Sync {NonfairSync(int permits) {super(permits);}protected int tryAcquireShared(int acquires) {return nonfairTryAcquireShared(acquires);}}//Acquires a permit from this semaphore, blocking until one is available, //or the thread is Thread#interrupt interrupted.public void acquire() throws InterruptedException {//执行AQS的模版方法acquireSharedInterruptibly()sync.acquireSharedInterruptibly(1);}//Releases a permit, returning it to the semaphore.public void release() {//执行AQS的模版方法releaseShared()sync.releaseShared(1);}//Synchronization implementation for semaphore.  //Uses AQS state to represent permits. Subclassed into fair and nonfair versions.abstract static class Sync extends AbstractQueuedSynchronizer {Sync(int permits) {//设置state的值为传入的令牌数setState(permits);}final int getPermits() {return getState();}final int nonfairTryAcquireShared(int acquires) {for (;;) {int available = getState();int remaining = available - acquires;if (remaining < 0 || compareAndSetState(available, remaining)) {return remaining;}}}protected final boolean tryReleaseShared(int releases) {for (;;) {int current = getState();int next = current + releases;if (next < current) {throw new Error("Maximum permit count exceeded");}if (compareAndSetState(current, next)) {return true;}}}...}...
}public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {...//Acquires in shared mode, aborting if interrupted.//Implemented by first checking interrupt status, then invoking at least once #tryAcquireShared, returning on success.//Otherwise the thread is queued, possibly repeatedly blocking and unblocking,//invoking #tryAcquireShared until success or the thread is interrupted.public final void acquireSharedInterruptibly(int arg) throws InterruptedException {if (Thread.interrupted()) {throw new InterruptedException();}//执行Semaphore的内部类Sync的子类实现的tryAcquireShared()方法,抢占共享锁if (tryAcquireShared(arg) < 0) {//执行AQS的doAcquireSharedInterruptibly()方法doAcquireSharedInterruptibly(arg);}}//Releases in shared mode.  //Implemented by unblocking one or more threads if #tryReleaseShared returns true.public final boolean releaseShared(int arg) {//执行Semaphore的内部类Sync实现的tryReleaseShared()方法,释放共享锁if (tryReleaseShared(arg)) {//执行AQS的doReleaseShared()方法doReleaseShared();return true;}return false;}...
}

6.Semaphore的令牌获取过程

(1)Semaphore的令牌获取过程

(2)Semaphore的公平策略

(3)Semaphore的非公平策略

(4)tryAcquireShared()后的处理

(1)Semaphore的令牌获取过程

在调用Semaphore的acquire()方法获取令牌时:首先会执行AQS的模版方法acquireSharedInterruptibly(),然后执行Sync子类实现的tryAcquireShared()方法来抢占锁。如果抢占锁失败,则执行AQS的doAcquireSharedInterruptibly()方法。该方法会将当前线程封装成Node结点并加入等待队列,然后挂起线程。

(2)Semaphore的公平策略

在执行Sync子类FairSync的tryAcquireShared()方法尝试获取令牌时,先通过AQS的hasQueuedPredecessors()判断是否已有线程在等待队列中。如果已经有线程在等待队列中,那么当前线程获取令牌就必然失败。否则,就递减state的值 + 判断state是否小于0 + CAS设置state的值。

(3)Semaphore的非公平策略

在执行Sync子类NonfairSync的tryAcquireShared()方法尝试获取令牌时,则会直接执行Sync的nonfairTryAcquireShared()方法来获取令牌,也就是递减state的值 + 判断state是否小于0 + CAS设置state的值。

(4)tryAcquireShared()后的处理

不管公平策略还是非公平策略,对应的tryAcquireShared()方法都是通过自旋来抢占令牌(CAS设置state),直到令牌数不够时才会让tryAcquireShared()方法返回小于0的数值。然后触发执行AQS的doAcquireSharedInterruptibly()方法,该方法会将当前线程封装成Node结点并加入等待队列,然后挂起线程。

public class Semaphore implements java.io.Serializable {private final Sync sync;//Creates a Semaphore with the given number of permits and nonfair fairness setting.public Semaphore(int permits) {sync = new NonfairSync(permits);}static final class NonfairSync extends Sync {NonfairSync(int permits) {super(permits);}//以非公平锁的方式获取令牌protected int tryAcquireShared(int acquires) {//执行Sync的nonfairTryAcquireShared()方法return nonfairTryAcquireShared(acquires);}}static final class FairSync extends Sync {FairSync(int permits) {super(permits);}//以公平锁的方式获取令牌protected int tryAcquireShared(int acquires) {for (;;) {//如果已经有线程在等待队列中,那么就说明获取令牌必然失败if (hasQueuedPredecessors()) {return -1;}int available = getState();int remaining = available - acquires;if (remaining < 0 || compareAndSetState(available, remaining)) {return remaining;}}}}//Acquires a permit from this semaphore, blocking until one is available, //or the thread is Thread#interrupt interrupted.public void acquire() throws InterruptedException {//执行AQS的模版方法acquireSharedInterruptibly()sync.acquireSharedInterruptibly(1);}//Synchronization implementation for semaphore.  //Uses AQS state to represent permits. Subclassed into fair and nonfair versions.abstract static class Sync extends AbstractQueuedSynchronizer {Sync(int permits) {//设置state的值为传入的令牌数setState(permits);}final int getPermits() {return getState();}final int nonfairTryAcquireShared(int acquires) {for (;;) {int available = getState();int remaining = available - acquires;if (remaining < 0 || compareAndSetState(available, remaining)) {return remaining;}}}...}...
}public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {...//Acquires in shared mode, aborting if interrupted.//Implemented by first checking interrupt status, then invoking at least once #tryAcquireShared, returning on success.//Otherwise the thread is queued, possibly repeatedly blocking and unblocking,//invoking #tryAcquireShared until success or the thread is interrupted.public final void acquireSharedInterruptibly(int arg) throws InterruptedException {if (Thread.interrupted()) {throw new InterruptedException();}//执行Semaphore的内部类Sync的子类实现的tryAcquireShared()方法,抢占共享锁if (tryAcquireShared(arg) < 0) {//执行AQS的doAcquireSharedInterruptibly()方法doAcquireSharedInterruptibly(arg);}}//Queries whether any threads have been waiting to acquire longer than the current thread.public final boolean hasQueuedPredecessors() {Node t = tail; // Read fields in reverse initialization orderNode h = head;Node s;return h != t && ((s = h.next) == null || s.thread != Thread.currentThread());}//Acquires in shared interruptible mode.private void doAcquireSharedInterruptibly(int arg) throws InterruptedException {final Node node = addWaiter(Node.SHARED);//封装当前线程为Shared类型的Node结点boolean failed = true;try {//第一次循环r = -1,所以会执行AQS的shouldParkAfterFailedAcquire()方法//将node结点的有效前驱结点的状态设置为SIGNALfor (;;) {final Node p = node.predecessor();//node结点的前驱结点if (p == head) {int r = tryAcquireShared(arg);if (r >= 0) {setHeadAndPropagate(node, r);p.next = null; // help GCfailed = false;return;}}//执行shouldParkAfterFailedAcquire()方法设置node结点的前驱结点的状态为SIGNAL//执行parkAndCheckInterrupt()方法挂起当前线程if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) {throw new InterruptedException();}}} finally {if (failed) {cancelAcquire(node);}}}...
}

7.Semaphore的令牌释放过程

(1)Semaphore的令牌释放过程

(2)Semaphore的令牌释放本质

(1)Semaphore的令牌释放过程

在调用Semaphore的release()方法去释放令牌时:首先会执行AQS的模版方法releaseShared(),然后执行Sync实现的tryReleaseShared()方法来释放锁(累加state值)。如果释放锁成功,则执行AQS的doReleaseShared()方法去唤醒线程。

(2)Semaphore的令牌释放本质

Semaphore的release()方法释放令牌的本质就是对state字段进行累加,然后唤醒等待队列头结点的后继结点 + 唤醒传递来唤醒等待的线程。

注意:并非一定要执行acquire()方法的线程才能调用release()方法,任意一个线程都可以调用release()方法,也可以通过reducePermits()方法来减少令牌数。

public class Semaphore implements java.io.Serializable {private final Sync sync;//Creates a Semaphore with the given number of permits and nonfair fairness setting.public Semaphore(int permits) {sync = new NonfairSync(permits);}//Releases a permit, returning it to the semaphore.public void release() {//执行AQS的模版方法releaseShared()sync.releaseShared(1);}//Synchronization implementation for semaphore.  //Uses AQS state to represent permits. Subclassed into fair and nonfair versions.abstract static class Sync extends AbstractQueuedSynchronizer {Sync(int permits) {//设置state的值为传入的令牌数setState(permits);}//尝试释放锁,也就是对state值进行累加protected final boolean tryReleaseShared(int releases) {for (;;) {int current = getState();int next = current + releases;if (next < current) {throw new Error("Maximum permit count exceeded");}if (compareAndSetState(current, next)) {return true;}}}...}...
}public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {...    //Releases in shared mode.  //Implemented by unblocking one or more threads if #tryReleaseShared returns true.public final boolean releaseShared(int arg) {//执行Semaphore的内部类Sync实现的tryReleaseShared()方法,释放共享锁if (tryReleaseShared(arg)) {//执行AQS的doReleaseShared()方法,唤醒等待队列中的线程doReleaseShared();return true;}return false;}//Release action for shared mode -- signals successor and ensures propagation. //Note: For exclusive mode, release just amounts to calling unparkSuccessor of head if it needs signal.private void doReleaseShared() {for (;;) {//每次循环时头结点都会发生变化//因为调用unparkSuccessor()方法会唤醒doAcquireSharedInterruptibly()方法中阻塞的线程//然后阻塞的线程会在执行setHeadAndPropagate()方法时通过setHead()修改头结点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}//唤醒头结点的后继结点//唤醒的线程会在doAcquireSharedInterruptibly()方法中执行setHeadAndPropagate()方法修改头结点unparkSuccessor(h);} else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) {//如果ws = 0表示初始状态,则修改结点为PROPAGATE状态continue;//loop on failed CAS}}if (h == head) {//判断头结点是否有变化break;//loop if head changed}}}//Wakes up node's successor, if one exists.private void unparkSuccessor(Node node) {int ws = node.waitStatus;if (ws < 0) {compareAndSetWaitStatus(node, ws, 0);}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);}}...
}

8.同步屏障CyclicBarrier介绍

(1)CyclicBarrier的作用

(2)CyclicBarrier的基本原理

(1)CyclicBarrier的作用

CyclicBarrier的字面意思就是可循环使用的屏障。CyclicBarrier的主要作用就是让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障时屏障才会打开,接着才让所有被屏障拦截的线程一起继续往下执行。线程进入屏障是通过CyclicBarrier的await()方法来实现的。

(2)CyclicBarrier的基本原理

假设有3个线程在运行中都会调用CyclicBarrier的await()方法,而每个线程从开始执行到执行await()方法所用时间可能不一样,最终当执行时间最长的线程到达屏障时,会唤醒其他较早到达屏障的线程继续往下执行。

CyclicBarrier包含两个层面的意思:

一是Barrier屏障点,线程调用await()方法都会阻塞在屏障点,直到所有线程都到达屏障点后再放行。

二是Cyclic循环,当所有线程通过当前屏障点后,又可以进入下一轮的屏障点进行等待,可以不断循环。

9.CyclicBarrier的await()方法源码

(1)CyclicBarrier的成员变量

(2)CyclicBarrier的await()方法源码

(3)CountDownLatch和CyclicBarrier对比

(1)CyclicBarrier的成员变量

//A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point.  
//CyclicBarriers are useful in programs involving a fixed sized party of threads that must occasionally wait for each other. 
//The barrier is called cyclic because it can be re-used after the waiting threads are released.
public class CyclicBarrier {...private static class Generation {boolean broken = false;}private final ReentrantLock lock = new ReentrantLock();private final Condition trip = lock.newCondition();//用于线程之间相互唤醒private final int parties;//参与的线程数量private int count;//初始值是parties,每调用一次await()就减1private final Runnable barrierCommand;//回调任务private Generation generation = new Generation();...
}

CyclicBarrier是基于ReentrantLock + Condition来实现的。

parties表示每次要求到达屏障点的线程数,只有到达屏障点的线程数满足指定的parties数量,所有线程才会被唤醒。

count是一个初始值为parties的计数器,每个线程调用await()方法会对count减1,当count为0时会唤醒所有线程,并且结束当前的屏障周期generation,然后所有线程进入下一个屏障周期,而且count会恢复成parties。

(2)CyclicBarrier的await()方法源码

线程调用CyclicBarrier的await()方法时,会触发调用CyclicBarrier的dowait()方法。

CyclicBarrier的dowait()方法会对count计数器进行递减。如果count递减到0,则会调用CyclicBarrier的nextGeneration()唤醒所有线程,同时如果异步回调任务barrierCommand不为空,则会执行该任务。如果count还没递减到0,则调用Condition的await()方法阻塞当前线程。

被阻塞的线程,除了会被CyclicBarrier的nextGeneration()方法唤醒外,还会被Thread的interrupt()方法唤醒、被中断异常唤醒,而这些唤醒会调用CyclicBarrier的breakBarrier()方法。

在CyclicBarrier的nextGeneration()方法和CyclicBarrier的breakBarrier()方法中,都会通过Condition的signalAll()方法唤醒所有被阻塞等待的线程。

//A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point.  
//CyclicBarriers are useful in programs involving a fixed sized party of threads that must occasionally wait for each other. 
//The barrier is called cyclic because it can be re-used after the waiting threads are released.
public class CyclicBarrier {...private static class Generation {boolean broken = false;//用来标记屏障是否被中断}private final ReentrantLock lock = new ReentrantLock();private final Condition trip = lock.newCondition();//用于线程之间相互唤醒private final int parties;//参与的线程数量private int count;//初始值是parties,每调用一次await()就减1private final Runnable barrierCommand;//回调任务private Generation generation = new Generation();public CyclicBarrier(int parties, Runnable barrierAction) {if (parties <= 0) throw new IllegalArgumentException();this.parties = parties;this.count = parties;this.barrierCommand = barrierAction;}public CyclicBarrier(int parties) {this(parties, null);}//Waits until all #getParties have invoked await on this barrier.public int await() throws InterruptedException, BrokenBarrierException {try {//执行CyclicBarrier的dowait()方法return dowait(false, 0L);} catch (TimeoutException toe) {throw new Error(toe);}}//Main barrier code, covering the various policies.private int dowait(boolean timed, long nanos) throws InterruptedException, BrokenBarrierException, TimeoutException {final ReentrantLock lock = this.lock;lock.lock();//使用Condition需要先获取锁try {//获取当前的generationfinal Generation g = generation;//确认当前generation的barrier是否有效,如果generation的broken为true,则抛出屏障中断异常if (g.broken) {throw new BrokenBarrierException();}if (Thread.interrupted()) {breakBarrier();throw new InterruptedException();}//统计已经到达当前generation的线程数量int index = --count;//如果index为0,则表示所有线程都到达了屏障点if (index == 0) {boolean ranAction = false;try {final Runnable command = barrierCommand;if (command != null) {//触发回调command.run();}ranAction = true;//执行nextGeneration()方法唤醒所有线程,同时进入下一个屏障周期nextGeneration();return 0;} finally {if (!ranAction) {breakBarrier();}}}//loop until tripped, broken, interrupted, or timed out//如果index > 0,则阻塞当前线程for (;;) {try {if (!timed) {//通过Condition的await()方法,在阻塞当前线程的同时释放锁//这样其他线程就能获取到锁执行上面的index = --counttrip.await();} else if (nanos > 0L) {nanos = trip.awaitNanos(nanos);}} catch (InterruptedException ie) {if (g == generation && ! g.broken) {breakBarrier();throw ie;} else {Thread.currentThread().interrupt();}}if (g.broken) {throw new BrokenBarrierException();}if (g != generation) {return index;}if (timed && nanos <= 0L) {//中断屏障,设置generation.broken为truebreakBarrier();throw new TimeoutException();}}} finally {lock.unlock();}}//Updates state on barrier trip and wakes up everyone.//Called only while holding lock.private void nextGeneration() {//通过Condition的signalAll()唤醒所有等待的线程trip.signalAll();//还原countcount = parties;//进入新的generationgeneration = new Generation();}//Sets current barrier generation as broken and wakes up everyone.//Called only while holding lock.private void breakBarrier() {generation.broken = true;count = parties;//通过Condition的signalAll()唤醒所有等待的线程trip.signalAll();}...
}

(3)CountDownLatch和CyclicBarrier对比

一.CyclicBarrier可以被重用、可以响应中断

二.CountDownLatch的计数器只能使用一次,但可以通过reset()方法重置

10.使用CountDownLatch等待注册的完成

Hadoop HDFS(分布式存储系统)的NameNode分为主备两个节点,各个DataNode在启动时都会向两个NameNode进行注册,此时就可以使用CountDownLatch等待向主备节点注册的完成。

//DataNode启动类
public class DataNode {//是否还在运行private volatile Boolean shouldRun;//负责和一组NameNode(主NameNode + 备NameNode)通信的组件private NameNodeGroupOfferService offerService;//初始化DataNodeprivate void initialize() {this.shouldRun = true;this.offerService = new NameNodeGroupOfferService();this.offerService.start();  }//运行DataNodeprivate void run() {try {while(shouldRun) {Thread.sleep(10000);  }   } catch (Exception e) {e.printStackTrace();}}public static void main(String[] args) {DataNode datanode = new DataNode();datanode.initialize();datanode.run(); }
}//负责某个NameNode进行通信的线程组件
public class NameNodeServiceActor {//向某个NameNode进行注册public void register(CountDownLatch latch) {Thread registerThread = new RegisterThread(latch);registerThread.start(); }//负责注册的线程,传入一个CountDownLatchclass RegisterThread extends Thread {CountDownLatch latch;public RegisterThread(CountDownLatch latch) {this.latch = latch;}@Overridepublic void run() {try {//发送rpc接口调用请求到NameNode去进行注册System.out.println("发送请求到NameNode进行注册...");Thread.sleep(1000);  latch.countDown();  } catch (Exception e) {e.printStackTrace();}}}
}//负责跟一组NameNode(主NameNode + 备NameNode)进行通信的线程组件
public class NameNodeGroupOfferService {//负责跟NameNode主节点通信的ServiceActor组件private NameNodeServiceActor activeServiceActor;//负责跟NameNode备节点通信的ServiceActor组件private NameNodeServiceActor standbyServiceActor;//构造函数public NameNodeGroupOfferService() {this.activeServiceActor = new NameNodeServiceActor();this.standbyServiceActor = new NameNodeServiceActor();}//启动OfferService组件public void start() {//直接使用两个ServiceActor组件分别向主备两个NameNode节点进行注册register();}//向主备两个NameNode节点进行注册private void register() {try {CountDownLatch latch = new CountDownLatch(2);  this.activeServiceActor.register(latch); this.standbyServiceActor.register(latch); latch.await();//阻塞等待主备都完成注册System.out.println("主备NameNode全部注册完毕...");} catch (Exception e) {e.printStackTrace();  }}
}

11.使用CyclicBarrier将工作任务多线程分而治之

//输出结果:
//线程1执行自己的一部分工作...
//线程2执行自己的一部分工作...
//线程3执行自己的一部分工作...
//所有线程都完成自己的任务,可以合并结果了...
//最终结果合并完成,线程3可以退出...
//最终结果合并完成,线程1可以退出...
//最终结果合并完成,线程2可以退出...
public class CyclicBarrierDemo {public static void main(String[] args) {final CyclicBarrier barrier = new CyclicBarrier(3, new Runnable() {public void run() {System.out.println("所有线程都完成自己的任务,可以合并结果了...");}});new Thread() {public void run() {try {System.out.println("线程1执行自己的一部分工作...");barrier.await();System.out.println("最终结果合并完成,线程1可以退出...");} catch (Exception e) {e.printStackTrace();}}}.start();new Thread() {public void run() {try {System.out.println("线程2执行自己的一部分工作...");barrier.await();System.out.println("最终结果合并完成,线程2可以退出...");} catch (Exception e) {e.printStackTrace();}}}.start();new Thread() {public void run() {try {System.out.println("线程3执行自己的一部分工作...");barrier.await();System.out.println("最终结果合并完成,线程3可以退出...");} catch (Exception e) {e.printStackTrace();}}}.start();}
}

12.使用CyclicBarrier聚合服务接口的返回结果

当然也可以使用CountDownLatch来实现聚合服务接口的返回结果;

public class ApiServiceDemo {public Map<String, Object> queryOrders() throws Exception {final List<Object> results = new ArrayList<Object>();final Map<String, Object> map = new ConcurrentHashMap<String, Object>();CyclicBarrier barrier = new CyclicBarrier(3, new Runnable() {@Overridepublic void run() {map.put("price", results.get(0));   map.put("order", results.get(1)); map.put("stats", results.get(2));  }});//请求价格接口new Thread() {public void run() {try {System.out.println("请求价格服务..."); Thread.sleep(1000);  results.add(new Object());    barrier.await();} catch (Exception e) {e.printStackTrace();  } };}.start();//请求订单接口new Thread() {public void run() {try {System.out.println("请求订单服务..."); Thread.sleep(1000);  results.add(new Object());    barrier.await();} catch (Exception e) {e.printStackTrace();  } };}.start();//请求统计接口new Thread() {public void run() {try {System.out.println("请求订单统计服务..."); Thread.sleep(1000);  results.add(new Object());    barrier.await();} catch (Exception e) {e.printStackTrace();  } };}.start();while(map.size() < 3) {Thread.sleep(100);  }return map;}
}

13.使用Semaphore等待指定数量线程完成任务

可以通过Semaphore实现等待指定数量的线程完成任务才往下执行。

//输出结果如下:
//线程2执行一个计算任务
//等待1个线程完成任务即可...
//线程1执行一个计算任务
public class SemaphoreDemo {public static void main(String[] args) throws Exception {final Semaphore semaphore = new Semaphore(0);new Thread() {public void run() {try {Thread.sleep(2000);System.out.println("线程1执行一个计算任务");semaphore.release();} catch (Exception e) {e.printStackTrace();}}}.start();new Thread() {public void run() {try {Thread.sleep(1000);System.out.println("线程2执行一个计算任务");semaphore.release();} catch (Exception e) {e.printStackTrace();}}}.start();semaphore.acquire(1);System.out.println("等待1个线程完成任务即可...");}
}

相关文章:

JUC并发—7.AQS源码分析三

大纲 1.等待多线程完成的CountDownLatch介绍 2.CountDownLatch.await()方法源码 3.CountDownLatch.coutDown()方法源码 4.CountDownLatch总结 5.控制并发线程数的Semaphore介绍 6.Semaphore的令牌获取过程 7.Semaphore的令牌释放过程 8.同步屏障CyclicBarrier介绍 9.C…...

避坑:过早的文件结束符(EOF):解决“git clone龙蜥OS源码失败”的失败过程

避坑&#xff1a;过早的文件结束符&#xff08;EOF&#xff09;&#xff1a;解决“git clone龙蜥OS源码失败”的失败过程 安装Anolis OS 8.9 下载AnolisOS-8.9-x86_64-dvd.iso并安装。 使用uname -a查看内核版本为5.10.134-18.an8.x86_64。 [rootlocalhost cloud-kernel]# c…...

基于知识图谱的问答系统:后端Python+Flask,数据库Neo4j,前端Vue3(提供源码)

基于知识图谱的问答系统&#xff1a;后端PythonFlask&#xff0c;数据库Neo4j&#xff0c;前端Vue3 引言 随着人工智能技术的不断发展&#xff0c;知识图谱作为一种结构化的知识表示方式&#xff0c;逐渐成为问答系统的重要组成部分。本文将介绍如何构建一个基于知识图谱的问答…...

日做力扣题2--215. 数组中的第K个最大元素

这道题我在做北京的一家教育公司的笔试时出现过&#xff0c;且题目里直接要求使用快排做&#xff0c;所以我也使用快排做的。 题目&#xff1a; 给定整数数组 nums 和整数 k&#xff0c;请返回数组中第 k 个最大的元素。 请注意&#xff0c;你需要找的是数组排序后的第 k 个最…...

centos8 使用yum安装程序出现报错

在执行yum指令出现源更新不了Could not resolve host: mirrorlist.centos.org&#xff1b; Unknown error问题 yum -y update结果 Errors during downloading metadata for repository appstream: - Curl error (6): Couldnt resolve host name for http://mirrorlist.centos…...

linux系统搭建DNS服务器、详细知识讲解

DNS服务器系统为rocky9.5&#xff0c; 1、安装DNS dnf -y install bind bind-utilsbind软件包 BIND 是一个开源的 DNS 服务器软件&#xff0c;广泛用于域名解析服务。 配置管理&#xff1a; 权威 DNS 服务器&#xff08;Authoritative DNS&#xff09;&#xff1a;为特定域名…...

【部署优化篇四】《DeepSeek移动端优化:CoreML/TFLite实战对比》

手机里的AI助手能秒速回答你的问题,游戏人物能实时追踪你的表情变化,这些酷炫功能的背后都离不开移动端机器学习框架的支撑。今天我们就来撕开两个当红炸子鸡框架CoreML和TFLite的神秘面纱,看看它们在模型优化这件事上到底藏着哪些独门绝技。 一、移动端优化的生存法则 在…...

DeepSeek联网搜索

deepseek 0、前言1、未联网2、联网2.1 SerpAPI2.2 SerpAPIDeepseek 0、前言 为获取最新消息&#xff0c;需给deepseek联网 1、未联网 from dotenv import load_dotenv from langchain_deepseek import ChatDeepSeekload_dotenv()# 1、模型 model ChatDeepSeek(model"d…...

pt100 2线和3线的区别?

3线比2线更稳定一些&#xff1b; 在电路中&#xff0c;b和c是不连接在一起的&#xff1b; 测试的时候&#xff0c;b和c是接在一起的&#xff0c;也就是说pt100中b和c是连接在一起的 3线比2线多一个反馈&#xff1b; 平时测试的时候&#xff0c;测试一下ab或者ac 都是一样的…...

ollama-chat-ui-vue,一个可以用vue对接ollama的开源项目,可接入deepSeek

ollama-chat-ui-vue 使用vue3 vite elementUi 搭建的前端chat,通过ollama可与模型对话,目前支持独立思考,切换模型(联网查询后续支持) github地址&#xff1a;ollama-chat-ui-vue 制作不易github点点star&#xff0c;谢谢 前置工作 安装ollama,ollama官网地址 安装完olla…...

hot100-3、438、560、239、240、160、234(2简3中1难)

滑窗问题↓ 3. 无重复字符的最长子串&#xff08;中等&#xff09; 方法一、滑动窗口 数组结合哈希表ascii码&#xff0c;滑动出口。其实可以优化为left Math.max(left,map.get(s.charAt(i)) 1)&#xff0c;数组的话就是全部初始化为-1&#xff0c;用来计算最新下标而不是…...

深入理解 Java 反射机制:获取类信息与动态操作

在 Java 编程中&#xff0c;反射&#xff08;Reflection&#xff09;是一种强大的机制&#xff0c;允许程序在运行时动态地获取类的信息并操作类的属性、方法和构造器。反射是 Java 动态语言特性的核心&#xff0c;广泛应用于框架开发、插件系统、序列化和反序列化等领域。本文…...

Redis 主从复制

概念 在分布式系统中为了解决单点问题&#xff0c;通常会把数据复制多个副本部署到其他服务器&#xff0c;满⾜故障恢复和负载均衡等需求。Redis 也是如此&#xff0c;它提供了复制的功能&#xff0c;实现了相同数据的多个 Redis 副本&#xff0c;通过一个主节点&#xff08;ma…...

Unity中NavMesh的使用 及其 导出给java服务端进行寻路

1.先添加 AI Navigation组件 2.Windows-->AI-->Navigation(Obsolete) 这样子就可以看到烘焙按钮 3.将物体标记为行走和不可行走 4.添加一个Plane和一些球体&#xff0c;并把需要形成NavMesh的物体选择为静态 // 因为只能烘焙静态的 之后可以看出烘焙后&#xff0c;看着被…...

【含文档+PPT+源码】基于微信小程序的猎兔汽车保养维修美容服务平台的设计与实现

项目介绍 本课程演示的是一款基于微信小程序的猎兔汽车保养维修美容服务平台的设计与实现&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 1.包含&#xff1a;项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从零开始部…...

iOS App的启动与优化

App的启动流程 App启动分为冷启动和热启动 冷启动&#xff1a;从0开始启动App热启动&#xff1a;App已经在内存中&#xff0c;但是后台还挂着&#xff0c;再次点击图标启动App。 一般对App启动的优化都是针对冷启动。 App冷启动可分为三个阶段&#xff1a; dyld&#xff1a…...

一周学会Flask3 Python Web开发-request请求钩子(Hook)

锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 有时候我们业务需求对请求做一些鉴权&#xff0c;日志&#xff0c;统计分析等功能&#xff0c;这时候可以对请求进行预处理( …...

git clone

方法一&#xff08;替换URL&#xff09; git clone https://gitclone.com/github.com/tendermint/tendermint.git 方法二&#xff08;设置git参数&#xff09; git config --global url."https://gitclone.com/".insteadOf https:// git clone https://github.co…...

nginx ngx_http_module(8) 指令详解

nginx ngx_http_module(8) 指令详解 nginx 模块目录 nginx 全指令目录 一、目录 1.1 模块简介 ngx_http_ssi_module&#xff1a;服务器端包含&#xff08;SSI&#xff09;模块&#xff0c;允许在HTML页面中插入其他内容或动态生成的内容。通过特殊的SSI指令&#xff08;如 …...

Apache Struts RCE (CVE-2024-53677)

前言 对目前的Apache Struts RCE (CVE-2024-53677)的poc进行总结&#xff0c;由于只能单个ip验证&#xff0c;所以自己更改一下代码&#xff0c;实现&#xff1a;多线程读取url验证并保存&#xff0c;更改为中文解释 免责声明 请勿利用文章内的相关技术从事非法测试&#xf…...

windows系统本地部署DeepSeek-R1全流程指南:Ollama+Docker+OpenWebUI

本文将手把手教您使用OllamaDockerOpenWebUI三件套在本地部署DeepSeek-R1大语言模型&#xff0c;实现私有化AI服务搭建。 一、环境准备 1.1 硬件要求 CPU&#xff1a;推荐Intel i7及以上&#xff08;需支持AVX2指令集&#xff09; 内存&#xff1a;最低16GB&#xff0c;推荐…...

前端:最简单封装nmp插件(组件)过程。

一、nmp使用 1、注册nmp账号&#xff1a;npm | Home 2、创建插件名称文件夹&#xff0c;如&#xff1a; vue3-components 3、初始化一个package.json文件&#xff1a;nmp init npm init package.json配置用处介绍&#xff0c;如下&#xff1a; {// 包名&#xff0c;必须…...

百度搜索融合 DeepSeek 满血版,开启智能搜索新篇

百度搜索融合 DeepSeek 满血版&#xff0c;开启智能搜索新篇 &#x1f680; &#x1f539; 一、百度搜索全量接入 DeepSeek &#x1f539; 百度搜索迎来重要升级&#xff0c;DeepSeek 满血版全面上线&#xff01;&#x1f389; 用户在百度 APP 搜索后&#xff0c;点击「AI」即…...

导出指定文件夹下的文件结构 工具模块-Python

python模块代码 import os import json import xml.etree.ElementTree as ET from typing import List, Optional, Dict, Union from pathlib import Path class DirectoryTreeExporter:def __init__(self,root_path: str,output_file: str,fmt: str txt,show_root: boo…...

V4L2驱动之UVC

以下是关于V4L2摄像头驱动框架与UVC协议的关联分析&#xff0c;从内核驱动到用户空间的完整视角&#xff1a; 1. V4L2驱动框架核心架构 关键组件&#xff1a; 核心层 (V4L2 Core) v4l2_device&#xff1a;设备的总入口&#xff0c;管理所有子组件video_device&#xff1a;对应…...

【Linux】匿名管道的应用场景-----管道进程池

目录 一、池化技术 二、简易进程池的实现&#xff1a; Makefile task.h task.cpp Initchannel函数&#xff1a; 创建任务&#xff1a; 控制子进程&#xff1a; 子进程执行任务&#xff1a; 清理收尾&#xff1a; 三、全部代码&#xff1a; 前言&#xff1a; 对于管…...

umi react+antd 判断渲染消息提示、input搜索、多选按钮组

记得map里返回的每层遍历结构都要带上key&#xff08;图里没加&#xff0c;最近在接手react&#xff0c;熟悉中......

Windows桌面系统管理5:Windows 10操作系统注册表

Windows桌面系统管理0&#xff1a;总目录-CSDN博客 Windows桌面系统管理1&#xff1a;计算机硬件组成及组装-CSDN博客 Windows桌面系统管理2&#xff1a;VMware Workstation使用和管理-CSDN博客 Windows桌面系统管理3&#xff1a;Windows 10操作系统部署与使用-CSDN博客 Wi…...

华为昇腾 910B 部署 DeepSeek-R1 蒸馏系列模型详细指南

本文记录 在 华为昇腾 910B(65GB) * 8 上 部署 DeepSeekR1 蒸馏系列模型&#xff08;14B、32B&#xff09;全过程与测试结果。 NPU&#xff1a;910B3 (65GB) * 8 &#xff08;910B 有三个版本 910B1、2、3&#xff09; 模型&#xff1a;DeepSeek-R1-Distill-Qwen-14B、DeepSeek…...

文献阅读 250219-Global water availability boosted by vegetation-driven changes (1)

Global water availability boosted by vegetation-driven changes in atmospheric moisture transport 来自 <https://www.nature.com/articles/s41561-022-01061-7> ## Abstract: 全球水资源的可用性是气候变化研究中的重要议题&#xff0c;尤其是随着气候变化的加剧&a…...

蓝桥杯篇---超声波距离测量频率测量

文章目录 简介第一部分&#xff1a;超声波的简介工作原理1.发射超声波2.接收反射波3.计算时间差4.计算距离 硬件连接1.Trig2.Echo 示例代码代码说明注意事项1.声速2.延时精度3.硬件连接 第二部分&#xff1a;频率测量简介频率测量原理1.信号输入2.计数3.计算频率 硬件连接示例代…...

【玩转 Postman 接口测试与开发2_020】(完结篇)DIY 实战:随书示例 API 项目本地部署保姆级搭建教程(含完整调试过程)

《API Testing and Development with Postman》最新第二版封面 文章目录 最新版《Postman 接口测试与开发实战》示例 API 项目本地部署保姆级搭建教程1 前言2 准备工作3 具体部署3.1 将项目 Fork 到自己名下3.2 创建虚拟环境并安装依赖3.3 初始运行与项目调试 4 示例项目的用法…...

LearnOpenGL——高级OpenGL(下)

教程地址&#xff1a;简介 - LearnOpenGL CN 高级数据 原文链接&#xff1a;高级数据 - LearnOpenGL CN 在OpenGL中&#xff0c;我们长期以来一直依赖缓冲来存储数据。本节将深入探讨一些操作缓冲的高级方法。 OpenGL中的缓冲本质上是一个管理特定内存块的对象&#xff0c;它…...

wangEditor 编辑器 Vue 2.0 + Nodejs 配置

资料 Vue2.0 版本的安装&#xff1a;https://www.wangeditor.com/v5/for-frame.html#%E4%BD%BF%E7%94%A8上传图片配置&#xff1a;https://www.wangeditor.com/v5/menu-config.html#%E4%B8%8A%E4%BC%A0%E5%9B%BE%E7%89%87 安装步骤 1.安装界面基础部分 <!-- 富文本编辑器…...

机器学习·数据处理

前言 对于大规模数据&#xff0c;我们经常会使用python内置函数或者编写脚本进行批量化处理&#xff0c;从而提高后续使用算法的效率。 1. 正则表达式 定义&#xff1a;用于检索、替换符合某个模式的文本&#xff0c;是文本预处理常用技术。基本语法 符号描述.匹配除换行符 …...

如何在Bigemap Pro中用线分割面、挖空

有时候需要以一条线为界对面元素进行分割或者是需要在一个面元素里面挖空一个面形状的洞&#xff0c;对此需求可以使用bigemap pro工具实现&#xff0c;这里为你介绍一下具体的操作方法。 【一】画线分割面 第一步&#xff1a;现在这是一个不规则多边形&#xff0c;想要以手动…...

网络安全入门攻击与防御实战(四)

漏洞利用&#xff1a;永恒之蓝&#xff08;MS17-010&#xff09;与同类漏洞解析 1 永恒之蓝&#xff08;MS17-010&#xff09;漏洞背景 &#xff08;1&#xff09;漏洞信息 CVE编号&#xff1a;CVE-2017-0143 ~ CVE-2017-0148 影响范围&#xff1a;Windows XP ~ Windows 201…...

DeepSeek 接入PyCharm实现AI编程!(支持本地部署DeepSeek及官方DeepSeek接入)

前言 在当今数字化时代&#xff0c;AI编程助手已成为提升开发效率的利器。DeepSeek作为一款强大的AI模型&#xff0c;凭借其出色的性能和开源免费的优势&#xff0c;成为许多开发者的首选。今天&#xff0c;就让我们一起探索如何将DeepSeek接入PyCharm&#xff0c;实现高效、智…...

CF1801D

CF1801D 题目大意&#xff1a; n n n 个顶点&#xff0c; m m m 条边的图。你一开始在起点 1&#xff0c;拥有 P P P 枚硬币&#xff0c;通过每条 ( i , j ) (i,j) (i,j) 边都需要花费一定的硬币 s ( i , j ) s(i,j) s(i,j)。但你在每个城市 i i i 都可以打工赚硬币 w i w…...

大厂算法面试常见问题总结:高频考点与备战指南

在大厂算法面试中&#xff0c;数据结构与算法是必考的核心内容。 无论是校招还是社招&#xff0c;算法题的表现往往决定了面试的成败。 为了帮助大家更好地备战&#xff0c;本文总结了大厂算法面试中的高频考点&#xff0c;并提供了详细的备战建议&#xff0c;助你轻松应对面…...

【R语言】主成分分析与因子分析

一、主成分分析 主成分分析&#xff08;Principal Component Analysis, PCA&#xff09;是一种常用的无监督数据降维技术&#xff0c;广泛应用于统计学、数据科学和机器学习等领域。它通过正交化线性变换将&#xff08;高维&#xff09;原始数据投影到一个新的坐标系&#xff…...

解锁 AIoT 无限可能,乐鑫邀您共赴 Embedded World 2025

2025 年 3 月 11-13 日&#xff0c;全球规模最大的嵌入式展览会——Embedded World 2025 将在德国纽伦堡盛大开幕。作为物联网和嵌入式技术领域的领先企业&#xff0c;乐鑫信息科技 (688018.SH) 将展示在 AI LLM、HMI、双频 Wi-Fi 6、低功耗 MCU 和 Matter 等领域的最新技术及解…...

人工智能基础之数学基础:01高等数学基础

函数 极限 按照一定次数排列的一列数:“&#xff0c;“,…,"…&#xff0c;其中u 叫做通项。 对于数列{Un}如果当n无限增大时&#xff0c;其通项无限接近于一个常数A&#xff0c;则称该数列以A为极限或称数列收敛于A&#xff0c;否则称数列为发散&#xff0c; 极限值 左…...

【从0做项目】Java搜索引擎(8) 停用词表 正则

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 文章导读 零&#xff1a;项目结果展示 一&#xff1a;前引 二&#xff1a;停用词表 1&#xff1a;…...

线程和进程的区别

如果说一个服务器同一时刻收到了许多请求&#xff0c;针对每一个请求&#xff0c;操作系统都会产生一个进程&#xff0c;从而给这个请求提供一些服务 &#xff0c;返回响应&#xff0c;如果请求处理完了&#xff0c;这个进程就要销毁了&#xff01;如果请求很多的话&#xff0c…...

深入理解 QObject的作用

QObject 作为 Qt 库中所有对象的基类&#xff0c;其地位无可替代。几乎 Qt 框架内的每一个类&#xff0c;无论是负责构建用户界面的 QWidget&#xff0c;还是专注于数据处理与呈现的 QAbstractItemModel&#xff0c;均直接或间接继承自 QObject。这种继承体系赋予 Qt 类库高度的…...

解码 NLP:从萌芽到蓬勃的技术蜕变之旅

内容概况&#xff1a; 主要讲述NLP专栏的内容和NLP的发展及其在现代生活中的广泛应用。课程强调实践为主、理论为辅的学习方法&#xff0c;并通过多个生活场景展示了NLP技术的实际应用&#xff0c;如对话机器人、搜索引擎、翻译软件、电商推荐和智能客服等。 这边我就不多做自我…...

【核心算法篇十五】《深度解析DeepSeek遗传算法:让超参数调优从“玄学”变“科学”的终极指南》

引言:超参数调优的“炼丹困局”与破局之路 在机器学习的世界里,调参工程师常被戏称为"炼丹师"——面对动辄几十个超参数的复杂模型,我们就像古代术士守着炼丹炉,不断尝试各种参数组合,期待偶然炼出"仙丹"。传统网格搜索(Grid Search)需要遍历所有可…...

Python—变量、基本数据类型、类型的转换

文章目录 Python—变量、基本数据类型1 格式化输出2 号的使用3 变量的数据类型4 type() 函数的使用5 数据类型的基本介绍5.1 int 整型5.2 float 浮点类型5.3 bool 布尔类型5.4 str 字符串类型5.5 字符串驻留机制5.6 数据类型的转换&#xff08;1&#xff09;隐式转换&#xff…...

启明星辰规则库下载

启明星辰规则库下载 一、脚本介绍 1、背景介绍 因为项目上有启明星辰的安全设备、并且在内网无法直接连接互联网进行在线升级&#xff0c;必须使用离线升级模式&#xff0c;下载规则库升级&#xff0c;每月一更有点繁琐&#xff0c;所以写了这个b脚本&#xff0c;偷懒一下&a…...