计数器
CountDownLatch:减法计数器
可以用来倒计时,当两个线程同时执行时,如果一个线程优先执行,可以使用计数器当计数器清零的时候,再让另一个线程执行。
import java.util.concurrent.CountDownLatch;
public class CountDownLatchTest {
public static void main(String[] args) {
CountDownLatch count = new CountDownLatch(400);
new Thread(()->{
for (int i = 0; i < 400; i++) {
System.out.println("============Thread");
count.countDown();
}
}).start();
try {
count.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 400; i++) {
System.out.println("main-------------");
}
}
}
countDown():计数器减一(计数器参数是多少,countDown就需要执行多少次,否则未清零就不会唤醒其他线程。)
await():计数器停止,唤醒其他线程。
在实时系统中的使用场景
- 实现最大的并行性:有时我们想同时启动多个线程,实现最大程度的并行性。例如,我们想测试一个单例类。如果我们创建一个初始计数器为1的CountDownLatch,并让其他所有线程都在这个锁上等待,只需要调用一次countDown()方法就可以让其他所有等待的线程同时恢复执行。
- 开始执行前等待N个线程完成各自任务:例如应用程序启动类要确保在处理用户请求前,所有N个外部系统都已经启动和运行了。
- 死锁检测:一个非常方便的使用场景是你用N个线程去访问共享资源,在每个测试阶段线程数量不同,并尝试产生死锁。
CyclicBarrier:加法计数器
new CyclicBarrier(int parties, Runnable barrierAction),当计数等于parties时,执行BarrierAction中的任务。
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierTest {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(30,()->{
System.out.println("结束");
});
for (int i = 0; i < 100; i++) {
final int temp = i;
new Thread(()->{
System.out.println("---->"+temp);
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
await():试图唤醒当前线程。
有参数parties大于、小于、等于计数次数的三种情况。
- 大于:不会执行BarrierAction中的任务,且程序会一直等待。
- 等于:在计数等于parties后,会执行BarrierAction中的任务,程序不继续等待。
- 小于:
- 如果计数次数等于参数parties的整数倍,那么会在每次计数等于parities时执行BarrierAction中的任务,且会重新计数,再次计数等于parities时执行BarrierAction中的任务,程序执行不会一直等待。
- 如果计数次数等于参数parties的整数倍,那么会在每次计数等于parities时执行BarrierAction中的任务,且会重新计数,再次计数等于parities时执行BarrierAction中的任务,程序无限等待。
总结:计数次数需要等于参数parties的整数倍才不会出现程序无限等待状况。
Semaphore:计数信号量
实际开发中主要使用它来完成限流操作,限制可以访问某些资源的线程数量。
Semaphore只有三个操作:
- 初始化
- 获取许可
- 释放
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreTest {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(5);
for (int i = 0; i < 15; i++) {
new Thread(()->{
try {
semaphore.acquire();//获取许可
System.out.println(Thread.currentThread().getName()+"进店");
TimeUnit.SECONDS.sleep(5);
System.out.println(Thread.currentThread().getName()+"出店");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();//释放许可
}
},Integer.toString(i)).start();
}
}
}
该程序模拟有15个顾客,店内同时只能接待5个顾客。
从线程上来讲,每个线程在执行的时候,首先需要获取信号量,只有获取到资源才可以执行,执行完毕之后需要释放资源,留给下一个线程。