主要步骤
-
创建一个线程池
ExecutorService service = Executors.newFixedThreadPool(20);
-
创建任务
Runnable task = () -> { // 具体实现 };
-
提交多个任务到线程池
for (int i = 0; i < 100000; i++) { service.submit(task); }
-
关闭线程池
service.shutdown();
-
等待所有任务完成
service.awaitTermination(1, TimeUnit.MINUTES);
示例一
以下是一个示例,使用多线程验证 IdGenerator
的 generateId
方法的准确性,测试其并发场景下是否会生成重复的 id。
import java.util.concurrent.*;
import java.util.*;
public class Test {
public static void main(String[] args) throws InterruptedException {
// 创建一个线程池
ExecutorService service = Executors.newFixedThreadPool(10);
// 创建一个用于存储生成的 ID 的集合
Set<String> idSet = Collections.synchronizedSet(new HashSet<>());
// 创建一个名为 IdGenerator 的类,其中包含一个 generateId 方法
class IdGenerator {
String generateId() {
// 在这里实现你的 ID 生成逻辑
return UUID.randomUUID().toString();
}
}
IdGenerator generator = new IdGenerator();
// 创建一个任务,该任务调用 ID 生成方法并将结果添加到集合中
Runnable task = () -> {
String id = generator.generateId();
if (idSet.contains(id)) {
System.out.println("重复的 ID: " + id);
} else {
idSet.add(id);
}
};
// 提交多个任务到线程池
for (int i = 0; i < 10000; i++) {
service.submit(task);
}
// 关闭线程池
service.shutdown();
// 等待所有任务完成
service.awaitTermination(1, TimeUnit.MINUTES);
System.out.println("生成的 ID 数量: " + idSet.size());
}
}
这个程序会创建一个固定大小的线程池,并提交多个任务到线程池。每个任务都会调用 generateId
方法并将生成的 ID 添加到一个集合中。如果生成的 ID 已经存在于集合中,那么程序会打印出重复的 ID。最后,程序会打印出生成的 ID 的数量。
请注意,这只是一个基本的示例,你可能需要根据你的具体需求进行修改。例如,你可能需要调整线程池的大小,任务的数量,或者 generateId
方法的实现。此外,如果你的 generateId
方法不是线程安全的,你可能需要添加适当的同步机制。如果你的 generateId
方法可能抛出异常,你可能需要添加适当的错误处理代码。最后,你可能需要添加更多的逻辑来更详细地检查生成的 ID(例如,检查 ID 的格式或长度)。
示例二
首先,假设你的 ID 生成方法如下:
public class IdGenerator {
public synchronized String generateUniqueId() {
// 这里是生成唯一 ID 的逻辑,这里只是一个示例
return UUID.randomUUID().toString();
}
}
然后,编写测试类和测试方法:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.HashSet;
import java.util.Set;
public class IdGeneratorTest {
private static final IdGenerator idGenerator = new IdGenerator();
private static final int THREAD_COUNT = 100; // 线程数量
private static final int ID_COUNT = 1000; // 每个线程生成的 ID 数量
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT);
Set<String> allIds = new HashSet<>(); // 用于存储所有生成的 ID,以检查重复
// 提交任务到线程池
for (int i = 0; i < THREAD_COUNT; i++) {
Runnable worker = new IdGeneratorTask(allIds, ID_COUNT);
executorService.submit(worker);
}
// 所有任务完成之前,主线程等待
executorService.shutdown();
if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
System.out.println("一些任务未完成执行。");
} else {
System.out.println("所有 ID 已生成并检查完毕。");
}
// 检查是否有重复的 ID
if (allIds.size() == THREAD_COUNT * ID_COUNT) {
System.out.println("测试通过:没有发现重复的 ID。");
} else {
System.out.println("测试失败:发现了重复的 ID。");
}
}
static class IdGeneratorTask implements Runnable {
private final Set<String> allIds;
private final int idCount;
public IdGeneratorTask(Set<String> allIds, int idCount) {
this.allIds = allIds;
this.idCount = idCount;
}
@Override
public void run() {
for (int i = 0; i < idCount; i++) {
String id = idGenerator.generateUniqueId();
synchronized (allIds) {
if (!allIds.add(id)) {
System.out.println("发现重复的 ID: " + id);
}
}
}
}
}
}
这个程序创建了一个 IdGenerator
类,其中包含一个同步方法 generateUniqueId
用于生成 ID。IdGeneratorTest
类中的 main
方法设置了一个线程池,并提交了多个任务到线程池中。每个任务都是生成指定数量的 ID 并将它们添加到一个共享的 HashSet
中。使用 HashSet
是因为它不允许重复元素,如果尝试添加重复的 ID,add
方法将返回 false
并打印出重复的 ID。
请注意,这个示例使用了 UUID
作为 ID 生成方法,它几乎可以保证全局唯一性。如果你使用的是其他类型的 ID 生成算法,你需要确保它在多线程环境下的线程安全性。
我们还可以对程序进行一些改进,程序中使用 synchronized
关键字来同步对 allIds
集合的访问,这可能会导致性能瓶颈。我们可以使用 ConcurrentHashMap
来替代 HashSet
,因为 ConcurrentHashMap
也能保证线程安全,并且性能更好。
下面是改进后的程序:
import java.util.concurrent.*;
import java.util.Map;
import java.util.UUID;
public class IdGenerator {
public String generateUniqueId() {
// 假设这里是你的 ID 生成逻辑
return UUID.randomUUID().toString();
}
}
public class IdGeneratorTest {
private static final IdGenerator idGenerator = new IdGenerator();
private static final int THREAD_COUNT = 100; // 线程数量
private static final int ID_COUNT = 1000; // 每个线程生成的 ID 数量
private static final Map<String, Integer> idCounts = new ConcurrentHashMap<>();
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT);
// 提交任务到线程池
for (int i = 0; i < THREAD_COUNT; i++) {
executorService.submit(new IdGeneratorTask());
}
executorService.shutdown();
if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
System.out.println("一些任务未完成执行。");
} else {
System.out.println("所有 ID 已生成并检查完毕。");
checkForDuplicates();
}
}
static class IdGeneratorTask implements Runnable {
@Override
public void run() {
for (int i = 0; i < ID_COUNT; i++) {
String id = idGenerator.generateUniqueId();
int count = idCounts.getOrDefault(id, 0) + 1;
idCounts.put(id, count);
}
}
}
private static void checkForDuplicates() {
idCounts.forEach((id, count) -> {
if (count > 1) {
System.out.printf("发现重复的 ID: %s (重复次数: %d)%n", id, count);
}
});
}
}
在这个改进的程序中,我们使用了 ConcurrentHashMap
来记录每个 ID 的出现次数。如果某个 ID 的出现次数大于 1,说明它是重复的,程序将打印出具体的重复 ID 及其重复次数。
请注意,这个示例使用了 UUID
作为 ID 生成方法,它几乎可以保证全局唯一性。如果你的 ID 生成算法不是基于 UUID
,那么你需要确保它在多线程环境下的线程安全性。此外,awaitTermination
方法的超时时间根据实际情况进行调整。
真实案例
错误的 generateId
方法实现:
synchronized String generateId() {
// 在这里实现你的 ID 生成逻辑
// 有问题的实现(并发场景下)
Random random = new Random();
Integer number = random.nextInt(9000) + 1000;
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
return sdf.format(new Date()) + String.valueOf(number);
}
改进的 generateId
方法实现:
synchronized String generateId() {
// 在这里实现你的 ID 生成逻辑
// 改进的实现:
String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("yyMMdd"));
Duration duration = Duration.between(LocalTime.of(0, 0, 0), LocalTime.now());
String secondsOfTimeStr = StringUtils.leftPad(String.valueOf(duration.getSeconds()), 5, "0");
String key = dateStr + secondsOfTimeStr;
String value = redisTemplate.opsForValue().get(key);
if (null == value) {
value = redisTemplate.opsForValue().increment(key).toString();
redisTemplate.expire(key, 60L, TimeUnit.SECONDS);
} else {
value = redisTemplate.opsForValue().increment(key).toString();
}
String x = key + StringUtils.leftPad(value, 4, "0");
return x;
}
至此,over~