CountDownLatch 是 Java 中并发包(java.util.concurrent)提供的一种同步工具,用于在多线程环境中协调多个线程之间的执行顺序。它的作用是允许一个或多个线程等待其他线程完成操作。
CountDownLatch 通过一个计数器来实现,计数器的初始值由用户设置,每当一个线程完成一项任务后,计数器的值就会减一。当计数器的值变为零时,等待在 CountDownLatch 上的线程就会被唤醒,可以继续执行。
以下是 CountDownLatch 的一些关键方法:
CountDownLatch(int count): 构造方法,传入计数器的初始值。
void countDown(): 计数器减一,表示一个线程完成了任务。
void await(): 等待计数器变为零,阻塞当前线程。
boolean await(long timeout, TimeUnit unit): 在指定的时间内等待计数器变为零,超时后返回 true,否则返回 false。
以下是一个简单的示例,演示了 CountDownLatch 的基本用法:
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
int numThreads = 3;
CountDownLatch latch = new CountDownLatch(numThreads);
for (int i = 0; i < numThreads; i++) {
new Thread(() -> {
// 模拟线程执行任务
System.out.println("Thread " + Thread.currentThread().getId() + " is executing.");
latch.countDown(); // 任务完成,计数器减一
}).start();
}
latch.await(); // 等待计数器变为零
System.out.println("All threads have completed their tasks.");
}
}
在这个示例中,创建了一个 CountDownLatch,并在多个线程中模拟执行任务,每个线程执行完任务后调用 countDown 方法。主线程通过 await 方法等待所有线程执行完任务后继续执行。这样,CountDownLatch 就实现了多个线程之间的协调。
CountDownLatch 的应用场景包括等待多个线程完成某个任务后再进行下一步操作、并行计算中等待多个计算任务完成、主线程等待多个子线程初始化完成等。
package com.test;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author 曹见朋
* @create 2023-11-12-15:38
*/
public class CountDownLatchTest {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
CountDownLatch countDownLatch = new CountDownLatch(5);
Random random = new Random();
String[] all = new String[5];
for (int i = 0; i < 5; i++) {
int k=i;
executorService.submit(()->{
for (int j = 0; j <= 100; j++) {
try {
Thread.sleep(random.nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
all[k] = j+"%";
System.out.print("\r"+ Arrays.toString(all));
}
countDownLatch.countDown();
});
}
try {
System.out.println("玩家正在进入游戏......游戏即将开始......");
System.out.println();
countDownLatch.await();
System.err.println("\n"+"\n"+"游戏开始!敌人还有3秒到达!请做好准备!");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
executorService.shutdown();
}
}
}
CyclicBarrier 是 Java 中并发包(java.util.concurrent)提供的另一种同步工具,用于在多线程环境中实现多个线程的同步点。与 CountDownLatch 类似,CyclicBarrier 也可以用于线程的协同,但其机制略有不同。
CyclicBarrier 的主要特点是可以重用。它允许一组线程相互等待,直到所有线程都达到某个同步点,然后继续执行。一旦所有线程达到同步点,CyclicBarrier 的内部计数器会被重置,并且所有线程可以继续执行下一轮同步。
以下是 CyclicBarrier 的一些关键方法:
CyclicBarrier(int parties): 构造方法,传入参与同步的线程数。
int await(): 调用线程到达同步点,等待其他线程。当所有线程都调用了 await 方法后,它们就会被释放,并且 CyclicBarrier 的内部计数器会被重置。
int await(long timeout, TimeUnit unit): 在指定的时间内等待其他线程。如果在超时时间内没有所有线程都到达同步点,调用线程会被释放,并返回一个表示等待状态的值。
以下是一个简单的示例,演示了 CyclicBarrier 的基本用法:
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierExample {
public static void main(String[] args) {
int numThreads = 3;
CyclicBarrier barrier = new CyclicBarrier(numThreads, () -> {
// 当所有线程都到达同步点时执行的动作
System.out.println("All threads have reached the barrier.");
});
for (int i = 0; i < numThreads; i++) {
new Thread(() -> {
try {
// 模拟线程执行任务
System.out.println("Thread " + Thread.currentThread().getId() + " is executing.");
barrier.await(); // 等待其他线程到达同步点
System.out.println("Thread " + Thread.currentThread().getId() + " continues execution.");
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
}
在这个示例中,创建了一个 CyclicBarrier,并在多个线程中模拟执行任务。每个线程执行完任务后调用 await 方法,等待其他线程到达同步点。一旦所有线程都到达同步点,就会执行在构造方法中传入的动作,并且所有线程会被释放,可以继续执行下一轮同步。
CyclicBarrier 的应用场景包括多个线程分阶段执行任务、多个线程分工合作执行任务等。
在游戏开发中,CyclicBarrier 可以用于实现多个玩家或角色在某个关键点进行同步,以确保所有玩家都准备好后再开始某个阶段的游戏。以下是一个典型的应用场景:
游戏多人联机对战场景:
假设有一个多人联机对战游戏,游戏中的每个玩家都需要准备好装备、选择角色等信息后,才能开始游戏。使用 CyclicBarrier 可以在游戏开始前创建一个同步点,确保所有玩家都准备就绪后才开始游戏。
package com.test.mythreadpool;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author 曹见朋
* @create 2023-11-12-16:27
*/
public class CyclicBarrierTest {
private static final int NUM_PLAYERS = 4;
private static final CyclicBarrier gameStartBarrier = new CyclicBarrier(NUM_PLAYERS, () -> {
System.out.println("All players are ready. Game starts!");
});
public static void main(String[] args) {
for (int i = 0; i < NUM_PLAYERS; i++) {
new Thread(() -> {
// 模拟玩家准备工作
System.out.println("Player " + Thread.currentThread().getId() + " is preparing.");
try {
// 等待其他玩家准备
gameStartBarrier.await();
// 开始游戏
System.out.println("Player " + Thread.currentThread().getId() + " starts the game.");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
在这个例子中,CyclicBarrier 被用于创建一个同步点 gameStartBarrier,在这个同步点上,所有玩家等待,直到所有玩家都准备好后,执行了 gameStartBarrier.await() 后,所有玩家同时开始游戏。
这种应用场景确保了游戏开始前所有玩家的同步,避免了一些玩家在游戏开始后还在准备中的问题,提升了游戏的整体体验。
CyclicBarrier 和 CountDownLatch 都是Java并发包提供的用于多线程协同的工具,但它们有一些关键区别:
可重用性:
计数器递减:
等待状态:
动作:
下面是一个简单的比较例子:
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
public class ComparisonExample {
public static void main(String[] args) throws InterruptedException {
int numThreads = 3;
// CyclicBarrier 示例
CyclicBarrier cyclicBarrier = new CyclicBarrier(numThreads, () -> {
System.out.println("CyclicBarrier: All threads have reached the barrier.");
});
for (int i = 0; i < numThreads; i++) {
new Thread(() -> {
System.out.println("CyclicBarrier: Thread is performing a task.");
try {
cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
Thread.sleep(2000); // 等待线程执行完成
// CountDownLatch 示例
CountDownLatch countDownLatch = new CountDownLatch(numThreads);
for (int i = 0; i < numThreads; i++) {
new Thread(() -> {
System.out.println("CountDownLatch: Thread is performing a task.");
countDownLatch.countDown();
}).start();
}
countDownLatch.await(); // 等待计数器为零
System.out.println("CountDownLatch: All threads have completed their tasks.");
}
}
在这个例子中,通过 CyclicBarrier 和 CountDownLatch 分别创建了两组线程,模拟了它们的使用方式。CyclicBarrier 在所有线程到达同步点时执行一个动作,而 CountDownLatch 在计数器为零时唤醒等待的线程。
最后,让我们以旅行团的情景来说明 CyclicBarrier 和 CountDownLatch 的区别:
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class TravelGroupWithCyclicBarrier {
private static final int NUM_TOURISTS = 5;
private static final CyclicBarrier arrivalBarrier = new CyclicBarrier(NUM_TOURISTS, () -> {
System.out.println("All tourists have arrived. Moving to the next attraction.");
});
public static void main(String[] args) {
for (int i = 0; i < NUM_TOURISTS; i++) {
new Thread(() -> {
// 游客到达当前景点
System.out.println("Tourist " + Thread.currentThread().getId() + " has arrived.");
try {
// 等待其他游客到达
arrivalBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
import java.util.concurrent.CountDownLatch;
public class TravelGroupWithCountDownLatch {
private static final int NUM_TOURISTS = 5;
private static final CountDownLatch preparationLatch = new CountDownLatch(NUM_TOURISTS);
public static void main(String[] args) {
for (int i = 0; i < NUM_TOURISTS; i++) {
new Thread(() -> {
// 游客完成准备工作
System.out.println("Tourist " + Thread.currentThread().getId() + " has completed preparations.");
// 减少准备计数器
preparationLatch.countDown();
}).start();
}
try {
// 等待所有游客完成准备工作
preparationLatch.await();
System.out.println("All tourists have completed preparations. Departing now!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}