CountDownLatch
使一个线程等待其他线程都执行完后再执行。
通过构造器初始化要等待的线程数,每个线程执行后调用 countDown() 方法使其计数器减一
调用 await() 方法的线程会被挂起,直到计数器减为0自动执行
public static void main(String[] args) throws ExecutionException, InterruptedException {
CountDownLatch latch = new CountDownLatch(5);
for (int i = 1; i <= 5; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName());
latch.countDown();
}, String.valueOf(i)).start();
}
latch.await();
System.out.println("end");
}
还可以设置等待时间,超过等待时间count还没变为0的话也继续执行
public boolean await(long timeout, TimeUnit unit)
CyclicBarrier
多个线程互相等待,直到到达同一个同步点,再继续一起执行。
通过构造器初始化一个阀门值和所有线程到达阀门后所要执行的线程任务,每个线程执行后调用 await() 使计数器加一,在计数器没达到阀门时所有线程都将等待,已经打开的屏障又有新线程加入的话还是会重新生效(循环)
public static void main(String[] args) throws InterruptedException {
int parties = 5;
ExecutorService executor = Executors.newFixedThreadPool(10);
CyclicBarrier cyclicBarrier = new CyclicBarrier(parties, () -> {
System.out.println("已集齐【" + parties + "】个木头");
});
for (int i = 1; i <= 10; i++) {
TimeUnit.SECONDS.sleep(1);
final int temp = i;
executor.submit(() -> {
try {
System.out.println("线程" + Thread.currentThread().getName() + " 已砍伐木头【x" + temp + "】");
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
});
}
TimeUnit.SECONDS.sleep(15);
executor.shutdownNow();
}
CountDownLatch和CyclicBarrier的区别
| CountDownLatch | CyclicBarrier |
|---|---|
| 减记数方式 | 加记数方式 |
| count为0时释放等待线程 | count达到阀门值时释放所有等待线程 |
| 一次性的 | 可循环利用 |
| 基于AQS实现 | 基于ReentrantLock锁和Condition实现 |
Semaphore
信号量,维持一组许可证。 通过 acquire() 获取许可证,如果没有可用的许可证就阻塞。通过 release() 释放许可证,潜在地释放阻塞方。
主要是用于多个共享资源的互斥使用;另一方面用于并发线程数量的控制(限流)
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(2);
ExecutorService executor = Executors.newFixedThreadPool(6);
for (int i = 1; i <= 10; i++) {
executor.submit(() -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " 占位");
TimeUnit.SECONDS.sleep(RandomUtil.randomInt(5));
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println(Thread.currentThread().getName() + " 离开");
semaphore.release();
}
});
}
}
Phaser
jdk1.7出的同步工具类:阶段器,有点像结合了CountDownLatch合CyclicBarrier,用来解决多个线程分阶段共同完成任务的情景问题。
在Phaser内有2个重要状态,分别是phase和party
phase就是阶段,当所有线程都调用了arrive*方法后,也就是当前阶段任务完成phase+1进入下一阶段任务,party就是当前管理的线程数。
可以通过继承Phaser类重写其onAdvance方法来实现自己的阶段管理,此方法返回true时即终止Phaser。
几个方法
// 初始化计数器,也就是完成当前阶段任务需要执行的线程数
public int bulkRegister(int parties)
// 到达phase,无需等待其他线程到达即开始执行下一阶段任务
public int arrive()
// 到达phase,阻塞等待其他线程到达才开始执行下一阶段任务
public int arriveAndAwaitAdvance()
// 到达phase,并且注销,也就是减少parties数
public int arriveAndDeregister()
// 当所有任务都到达phase后执行该方法,phase代表当前阶段 范围0~Integer.MAX_VALUE,registeredParties代表当前注册的线程数,该方法返回true时结束phaser
protected boolean onAdvance(int phase, int registeredParties)
案例
一对新人结婚办酒席邀请了五位宾客参加。酒席分为四阶段进行:第一阶段等待所有宾客到齐,第二阶段所有宾客到齐后开始吃饭,第三阶段所有宾客都酒足饭饱后陆续离开,第四阶段等所有宾客都离开了新人入洞房。每个阶段都必须等待上个阶段的任务完成后才得以进行。
public class PhaserDemo {
static Random r = new Random();
static MarriagePhaser phaser = new MarriagePhaser();
static void milliSleep(){
try {
TimeUnit.SECONDS.sleep(r.nextInt(5));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static class Person implements Runnable{
private final String name;
public Person(String name) {
this.name = name;
}
public void arrive(){
milliSleep();
System.out.println(name + " 到达现场!");
// 只有每个线程都执行了arriveAndAwaitAdvance才会使phase+1进入下一阶段
phaser.arriveAndAwaitAdvance();
}
public void eat(){
milliSleep();
System.out.println(name + " 吃饭!");
phaser.arriveAndAwaitAdvance();
}
public void leave(){
if (name.startsWith("p")) {
milliSleep();
System.out.println(name + " 离席!");
// arriveAndDeregister代表到达并且使parties-1
phaser.arriveAndDeregister();
}else {
phaser.arriveAndAwaitAdvance();
}
}
public void hug(){
if (!name.startsWith("p")) {
milliSleep();
System.out.println(name + " 洞房!");
phaser.arriveAndDeregister();
}
}
@Override
public void run() {
arrive();
eat();
leave();
hug();
}
}
static class MarriagePhaser extends Phaser{
// 每个阶段完成后都会进行回调 phase代表当前阶段 registeredParties代表注册的parties(线程)数 返回true时Phaser被终止
@Override
protected boolean onAdvance(int phase, int registeredParties) {
switch (phase){
case 0:
System.out.println("所有人都到齐了!" + registeredParties);
return false;
case 1:
System.out.println("所有人都吃完了!" + registeredParties);
return false;
case 2:
System.out.println("宾客都离开了!" + registeredParties);
return false;
case 3:
System.out.println("送入洞房" + registeredParties);
return true;
default:
return true;
}
}
}
public static void main(String[] args) {
// 初始化Parties的个数
phaser.bulkRegister(7);
// 五个宾客线程,一个新郎一个新娘
for (int i = 0; i < 5; i++) {
new Thread(new Person("p" + i)).start();
}
new Thread(new Person("新郎")).start();
new Thread(new Person("新娘")).start();
}
}
Exchanger
交换器,两个线程间交换数据用的。只能两两交换,只有交换完了才会继续玩下执行,否则就一直阻塞。
Exchanger<String> exchanger = new Exchanger<>();
new Thread(() -> {
String s = "T1";
try {
s = exchanger.exchange(s);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + s);
}, "T1").start();
new Thread(() -> {
String s = "T2";
try {
s = exchanger.exchange(s);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + s);
}, "T2").start();
LockSupport
不需要加锁即可实现线程的阻塞,提供了park和unpark两个方法来阻塞和解除阻塞线程,这是一个较为底层 的同步工具类,例如AQS就是调用它,在大多数并发控制的代码中并不会应用到它。
public static void main(String[] args) {
Thread thread = new Thread(() -> {
for (int i = 0; i < 10; i++) {
if (i == 5) {
LockSupport.park();
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
});
thread.start();
// 可以先于park进行,这样以上代码就不会阻塞
LockSupport.unpark(thread);
}
843

被折叠的 条评论
为什么被折叠?



