CountDownLatch
使一个线程等待其他线程各自执行完毕后再执行的辅助工具。
简单使用
public static void main(String[] args) throws InterruptedException {
List<User> list = new ArrayList<>();
//如果为0,就不会阻塞线程
CountDownLatch count = new CountDownLatch(list.size());
for (User user : list) {
new Thread(() -> {
user.setId(Thread.currentThread().getName());
//计数器减一
count.countDown();
}).start();
}
//等待计数器归零,最多等待20s
count.await(20, TimeUnit.SECONDS);
for (User user : list) {
System.out.println(user);
}
}
CyclicBarrier
让所有线程都完成后才会进行特定的行动。cyclic:循环,barrier:屏障
简单使用
public static void main(String[] args) throws InterruptedException {
CyclicBarrier cyclicBarrier = new CyclicBarrier(5, () -> {
System.out.println("run");
});
for (int i = 0; i < 5; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName());
try {
//每执行这个方法5次,就会运行cyclicBarrier中的Runnable线程
//如果不到5次,就会一直等待
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}, String.valueOf(i)).start();
}
}
CountDownLatch和CyclicBarrier
都能够实现线程之间的等待。
- CountDownLatch是减法计数器,计数减为0后不能重用。
- CyclicBarrier是加法计数器,计数减置0后可以复用。
- CountDownLatch则是直接利用AQS,采用共享锁来实现;CyclicBarrier内部是使用ReentrantLock和Condition来实现等待和唤醒操作。
Semaphore
可以控制同时访问资源的线程个数的工具。
本质是一把可以控制并发访问量的共享锁,超出访问量的线程就会在队列中挂起,是通过AbstractQueuedSynchronizer来实现的,里面实现了公平锁和非公平锁两种锁获取的机制(默认是非公平锁)。
简单使用
public static void main(String[] args) {
//保证最多有3个线程执行,限流
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 5; i++) {
new Thread(() -> {
try {
//相当于获得锁,如果已经到了最大值,等待到可以获得为止
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + ":acquire");
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + ":release");
//相当于释放锁,释放当前信号量,并唤醒其它线程
semaphore.release();
}
}, String.valueOf(i)).start();
}
}