并发编程 线程同步工具

本文将介绍并发编程中的线程同步工具。

一、CountDownLatch

1. 作用

适用于一个线程等待多个线程的场景,通过计数的方式解决问题。

2. 示例

假设有这样一个需求:当前线程需要等待线程 T1 和 T2 执行某些操作。

显然,我们可以这样做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Test {
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
Thread.sleep(1000);
System.out.println(1);
});
thread1.start();
Thread thread2 = new Thread(() -> {
Thread.sleep(500);
System.out.println(2);
});
thread2.start();
thread1.join();
thread2.join();
System.out.println(3);
}

}

但是当线程不会销毁时(例如通过线程池进行的多线程执行),join() 方法便无法使用。

可以通过 CountDownLatch 解决这一问题,具体使用方法如下:

  • 首先实例化 CountDownLatch,传入需要等待的线程数
  • 若需要等待的子线程执行完成,应该调用 countDownLatch.countDown() 方法使计数器递减
  • 在主线程中使用 countDownLatch.await() 方法,等待直至计数器归零
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Test2 {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(2);
Executor executor = Executors.newFixedThreadPool(2);
executor.execute(() -> {
Thread.sleep(1000);
System.out.println(1);
countDownLatch.countDown();
});
executor.execute(() -> {
Thread.sleep(1000);
System.out.println(1);
countDownLatch.countDown();
});
countDownLatch.await();
System.out.println(3);
}

}

二、CyclicBarrier

1. 作用

适用于多个线程 “同步前进” 场景。

2. 示例

假设有这样一个需求:线程 T1 和 T2 共同循环完成一个任务,且两个线程应该保持同步(T1 完成且 T2 完成后,才能进行下一轮循环)。

如果不引入额外的机制,假如 T1 与 T2 的每轮执行所需时间不同,则两个线程将彻底丧失同步,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Test {
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
while (true) {
Thread.sleep(1000);
System.out.println(1);
}
});
thread1.start();
Thread thread2 = new Thread(() -> {
while (true) {
Thread.sleep(500);
System.out.println(2);
}
});
thread2.start();
}

}

可以通过 CyclicBarrier 解决这一问题,具体使用方法如下:

  • 首先实例化 CyclicBarrier,传入 “同步前进” 的线程数
  • 若线程执行完成,应该调用 cyclicBarrier.await() 方法等待其它 “同步前进” 的线程执行完成
  • 当所有的线程都调用 cyclicBarrier.await() 方法后,cyclicBarrier 将重置,并允许所有线程继续执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Test2 {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(2);
Thread thread1 = new Thread(() -> {
while (true) {
Thread.sleep(1000);
System.out.println(1);
cyclicBarrier.await();
}
});
thread1.start();
Thread thread2 = new Thread(() -> {
while (true) {
Thread.sleep(500);
System.out.println(2);
cyclicBarrier.await();
}
});
thread2.start();
}

}

参考

  • Java 并发编程实战