Semaphore-控制并发线程数
- 信号量,用来限制能够同时访问共享资源的线程上限。
- Semaphore可以用来流量控制,在线程数大于资源数时,用来限制线程数;比如数据库连接池的改进
基本使用
@Slf4j
public final class Demo{
public static void main(String[] args){
// 创建Semaphore对象,限制为3
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 10; i++) {
new Thread(()->{
try {
// 表示从semaphore中获取共享资源获取许可
semaphore.acquire();
log.debug("get resource");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
// 释放资源
semaphore.release();
}
},"t"+i).start();
}
}
}
其他方法
int availablePermits()
:返回此信号量中当前可用的许可证数int getQueueLength()
:返回正在等待获取许可证的线程数boolean hasQueuedThreads()
:是否有线程正在等待获取许可证protected void reducePermits(int reduction)
:减少reduction个许可证protected Collection getQueuedThreads()
:返回所有获取许可证的线程集合
CountdownLatch-等待多线程完成
- 用来进行线程同步,等待一个或多个其他线程完成操作
- 其中构造参数用来初始化等待计数值,await()用来等待计数归零,countDown()用来让计数减一
- 首先创建一个等待计数,调用await进入阻塞,然后每个线程执行完毕就让计数减一,当计数为0时,线程将会被唤醒
@Slf4j
public final class Demo{
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(3);
new Thread(()->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("t1 finished");
countDownLatch.countDown();
},"t1").start();
new Thread(()->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("t2 finished");
countDownLatch.countDown();
},"t2").start();
new Thread(()->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("t3 finished");
countDownLatch.countDown();
},"t3").start();
countDownLatch.await();
log.debug("main resume");
}
}
CyclicBarrier-同步屏障
- CyclicBarrier字面意思是可循环使用的屏障,他要做的事是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行
- CyclicBarrier可以用于多线程计算数据,最后合并计算结果的场景
@Slf4j
public final class Demo{
public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
new Thread(()->{
try {
// 调用await方法表示已经到达了屏障
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
log.debug("t1 continue");
},"t1").start();
new Thread(()->{
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
log.debug("t2 continue");
},"t2").start();
Thread.sleep(3000);
cyclicBarrier.await();
log.debug("main continue");
}
}
CyclicBarrier和CountDownLatch的区别
- CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset方法重置,所以CyclicBarrier可以处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并且让线程重新执行
- CyclicBarrier还提供了其他方法,比如getNumberWaiting可以获得阻塞的线程数量、isBroken方法可以用来了解阻塞的线程是否被中断
Exchanger-线程间交换数据
Exchanger是一个用于线程间写作的工具类,进行线程间的数据交换。他提供一个同步点,在这个同步点,两个线程可以交换彼此的数据。
@Slf4j
public final class Demo{
public static void main(String[] args){
Exchanger<String> exchanger = new Exchanger<>();
new Thread(()->{
try {
String rec = exchanger.exchange("t1");
log.debug(rec);
} catch (InterruptedException e) {
e.printStackTrace();
}
},"t1").start();
new Thread(()->{
try {
String rec = exchanger.exchange("t2");
log.debug(rec);
} catch (InterruptedException e) {
e.printStackTrace();
}
},"t2").start();
}
}