之所以把这3个类放在一起,因为它们都是线程协作的辅助类。
- CountDownLatch,可以实现某一些线程,等待其他线程执行完之后,才可以执行
- CyclicBarrier,实现了多个线程间的相互等待,直到大家等待的条件满足了,就可以一起执行了
- Semaphore,是信号量,可以控制并发的线程数
一、CountDownLatch
CountDownLatch,更多的是用于等待前面的线程执行完了,后面的才可以执行,测试如下:
public class CountDownLatchTest {
public static void main(String[] args) {
int count = 3;
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < count; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+" is run,System.currentTimeMillis() = "+System.currentTimeMillis());
try {
Thread.sleep(1000);
// 计数减1
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
// 等待3个线程执行完成,才执行下面的任务
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(count+ " 个线程的都执行完了,该我执行了,System.currentTimeMillis() = "+System.currentTimeMillis());
}
}
测试结果:
Thread-0 is run,System.currentTimeMillis() = 1616429063878
Thread-1 is run,System.currentTimeMillis() = 1616429063878
Thread-2 is run,System.currentTimeMillis() = 1616429063878
3 个线程的都执行完了,该我执行了,System.currentTimeMillis() = 1616429064880
首先,count = 3,启动了3个线程,在每个线程执行完了之后,执行如下方法,进行计数器减1:
countDown();
而它的await()方法,会阻塞,直到计数从3减到0,才会继续执行后面的代码,所以3个子线程执行完了,才会继续执行主线程的代码。
二、CyclicBarrier
CyclicBarrier的作用,可以用来实现多个线程的相互等待,直到满足等待条件,你可以想象这个过程:跑步的时候,只有等大家各就各位做好准备之后,听了枪声才会一起跑。而CyclicBarrier的作用就是让确定数目的几个人准备好了,让它们一起跑,不能一个先跑,另外一个后跑。
测试代码:
public class CyclicBarrierTest {
public static void main(String[] args) {
int count = 3;
CyclicBarrier barrier = new CyclicBarrier(count);
for (int i = 0; i < count; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"开始执行,System.currentTimeMillis() = "+System.currentTimeMillis());
try {
barrier.await();
System.out.println(Thread.currentThread().getName()+"等待结束,一起执行,System.currentTimeMillis() = "+System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
测试结果:
Thread-0开始执行,System.currentTimeMillis() = 1616428984532
Thread-2开始执行,System.currentTimeMillis() = 1616428984532
Thread-1开始执行,System.currentTimeMillis() = 1616428984532
Thread-1等待结束,一起执行,System.currentTimeMillis() = 1616428984532
Thread-2等待结束,一起执行,System.currentTimeMillis() = 1616428984532
Thread-0等待结束,一起执行,System.currentTimeMillis() = 1616428984532
可以看到,count = 3,只有await()的线程数量为3时,才会继续执行各自后面的代码。
三、Semaphore
Semaphore是通过许可的数量来控制可以执行的线程数的,请看测试代码:
public class SemaphoreTest {
public static void main(String[] args) {
int permits = 2;
Semaphore semaphore = new Semaphore(permits);
for (int i = 0; i <10 ; i++) {
new Thread(() -> {
// 获取许可
try {
System.out.println(Thread.currentThread().getName()+",尝试获取许可");
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 执行业务代码
System.out.println(Thread.currentThread().getName()+",成功");
// 释放许可
semaphore.release();
}).start();
}
}
}
测试结果:
Thread-1,尝试获取许可
Thread-0,尝试获取许可
Thread-1,成功
Thread-2,尝试获取许可
Thread-0,成功
Thread-2,成功
Thread-3,尝试获取许可
Thread-3,成功
Thread-5,尝试获取许可
Thread-5,成功
Thread-4,尝试获取许可
Thread-4,成功
Thread-6,尝试获取许可
Thread-6,成功
Thread-9,尝试获取许可
Thread-9,成功
Thread-7,尝试获取许可
Thread-7,成功
Thread-8,尝试获取许可
Thread-8,成功
因为permits =2,说明同时只能有2个线程执行,如果把permits 改为1,那么它就只能是单线程的执行的,效果如下:
Thread-0,尝试获取许可
Thread-0,成功
Thread-1,尝试获取许可
Thread-1,成功
Thread-2,尝试获取许可
Thread-2,成功
Thread-3,尝试获取许可
Thread-3,成功
Thread-4,尝试获取许可
Thread-4,成功
Thread-5,尝试获取许可
Thread-5,成功
Thread-8,尝试获取许可
Thread-8,成功
Thread-9,尝试获取许可
Thread-9,成功
Thread-6,尝试获取许可
Thread-6,成功
Thread-7,尝试获取许可
Thread-7,成功