减法计数器
本质上是等待一系列线程完成它的任务的计数工具
允许一个或多个线程等待直到在其他线程中执行的一组操作完成的同步辅助。
使用给定计数初始化CountDownLatch
。 所述await
种方法阻塞,直到当前计数达到零由于的调用countDown()
方法,之后所有等待的线程被释放和任何后续调用await
立即返回。 这是一次性现象 - 计数无法重置。 如果您需要重置计数的版本,请考虑使用CyclicBarrier
。
CountDownLatch
是一种多功能同步工具,可用于多种用途。 初始化为计数为1的CountDownLatch
用作简单的开/关锁存器或门:所有调用await
的线程在门处等待,直到由调用countDown()
的线程打开它。 初始化为N的CountDownLatch
可用于使一个线程等待,直到N个线程完成某个动作,或者某个动作已完成N次。
CountDownLatch
一个有用属性是它不需要调用countDown
线程等待计数在继续之前达到零,它只是阻止任何线程继续通过await
直到所有线程都可以通过。
**示例用法:**这是一对类,其中一组工作线程使用两个倒计时锁存器:
实例:
package callable;
import java.util.concurrent.CountDownLatch;
// 计数器
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
// 倒计时,总数是六, 必须要执行任务的时候再使用
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; i < 6; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+" go out");
countDownLatch.countDown();// -1操作
},String.valueOf(i)).start();
}
countDownLatch.await(); // 等待计数器归零,然后再向下执行;
System.out.println("close the door");
}
}
原理:
countDownLatch.countDown(); // 数量-1
coutnDownLatch.await(); // 等待计数器归零,然后再向下执行
每次线程调用countDown()数量-1,假设计数器变为0,countDownLatch.await()就会被唤醒,继续执行
加法计数器
一种同步辅助工具,允许一组线程全部等待彼此到达公共障碍点。 CyclicBarriers在涉及固定大小的线程方的程序中很有用,这些线程必须偶尔等待彼此。 屏障称为*循环,*因为它可以在释放等待线程后重新使用。
CyclicBarrier
支持可选的Runnable
命令,该命令在每个障碍点运行一次,在聚会中的最后一个线程到达之后,但在释放任何线程之前。 在任何一方继续之前,此屏障操作对于更新共享状态非常有用。
package add;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CycliBarrierDemo {
public static void main(String[] args) {
/**
* 集齐七颗龙珠召唤神龙
*/
// 召唤龙珠的线程
// 有两个参数,第一个为数量,第二个为达到数量后开启的线程,直接使用runnable接口的lambda表达式
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
System.out.println("召唤神龙,成功");
});
for (int i = 1; i <= 7; i++) {
final int temp = i;
// lambda 能操作到 i 吗? ----> 不行,因为lambda表达式本质上是new了一个对象,实现了接口方法
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"收集了"+temp+"颗");
try {
cyclicBarrier.await();// 等待
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (BrokenBarrierException e) {
throw new RuntimeException(e);
}
}).start();
}
}
}
信号量
作用:
实现多个线程互斥地使用共享资源,并发限流,控制最大的线程数
原理:
acquire()方法,使线程获得资源,每有一个线程获得资源,信号量-1,若信号量为0,则剩余线程等待
release()方法,释放,会将当前的信号量释放并+1,然后唤醒等待的线程
new Semaphore(3); // 限定三个线程并发工作
semaphore.acquire(); // 让一个线程得到资源开始工作
semaphore.release(); // 释放当前抢占资源的线程,如果还有线程等待工作,换下一个线程进来
注:(来自文心一言)
semaphore.acquire()
操作是尝试获取一个或多个信号量。具体取决于你调用 acquire()
方法时传递的参数。
如果你调用 semaphore.acquire(n)
,其中 n
是你希望获取的信号量数量,那么它会尝试一次性获取 n
个信号量。如果当前可用的信号量数量少于 n
,那么这个操作将会阻塞,直到有足够的信号量可用。
如果你调用 semaphore.acquire()
并且不提供任何参数,那么它会尝试获取一个信号量。如果当前没有可用的信号量,那么这个操作也将会阻塞,直到有一个信号量可用。
总的来说,semaphore.acquire()
可以在一次调用中尝试获取一个或多个信号量,具体取决于你如何调用它。但是,它的行为并不是“一次性将所有信号量都分配完毕”,而是根据你传递的参数来决定获取多少个信号量。
package add;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreDemo {
public static void main(String[] args) {
// 参数:线程数量
// 限流的情况下可以使用
// 让线程有序地进行工作
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 6; i++) {
new Thread(()->{
try {
semaphore.acquire();// acquire() 得到
System.out.println(Thread.currentThread().getName()+"抢到车位");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+"离开车位");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
semaphore.release();// release() 释放
}
},String.valueOf(i)).start();
}
}
}