Java 意外的并发结果
Java unexpected concurrent result
在测试并发时,我发现了一些意想不到的事情。
使用 concurrentHashMap 和 AtomicLong 控制并发。
public class HumanRepository {
private final static Map<Long, Human> STORE = new ConcurrentHashMap<>();
private AtomicLong sequence = new AtomicLong();
public void save(Human human) {
STORE.put(sequence.incrementAndGet(), human);
}
public int size() {
return STORE.size();
}
public Long getSeq() {
return sequence.get();
}
}
我测试了多线程保存。
@Test
void name() throws NoSuchMethodException, InterruptedException {
final int threads = 3_500;
final ExecutorService es = Executors.newFixedThreadPool(threads);
final CountDownLatch count = new CountDownLatch(threads);
final HumanRepository repository = new HumanRepository();
for (int i = 0; i < threads; i++) {
try {
es.execute(() -> repository.save(new Human("aa")));
} finally {
count.countDown();
}
}
count.await();
System.out.println("seq = " + repository.getSeq());
System.out.println("size = " + repository.size());
}
我同时测试了3500个线程。我期望的结果是 seq 和 size 都是 3500。
但有时我会得到 seq=3499,size=3500。
那真是怪了。奇怪的是seq出来的不是3500,虽然size是3500,seq是3499也没意义
不知道为什么map里面的data number和seq不一样,3500出不来
**如果你在count.await();
之后做Thread.sleep(400L);
,奇怪的是,seq的值为3500
您实际上并不是在等待所有任务完成。也就是说,如果你得到3500/3500输出,那是偶然的。
具体来说,您在安排作业后减少主线程上的倒计时闩锁,而不是作业内部,一旦完成.这意味着您的 countdownlatch 基本上只是另一个不进行任何 inter-thread 通信的美化循环变量。尝试这样的事情:
for (int i = 0; i < threads; i++) {
es.execute(() -> {
repository.save(new Human("aa"));
count.countDown();
});
}
您在执行 HumanRepository.save() 的线程外调用 count.countDown()。所以可能主线程没有同步完成线程。
因此您可能会看到 repository.getSeq() 的结果,而一个线程是 运行。您可以尝试使用以下代码吗?
final int threads = 3_500;
final ExecutorService es = Executors.newFixedThreadPool(threads);
final CountDownLatch count = new CountDownLatch(threads);
final HumanRepository repository = new HumanRepository();
for (int i = 0; i < threads; i++) {
try {
es.execute(() -> {
repository.save(new Human("aa"));
count.countDown();
});
} finally {
}
}
count.await();
System.out.println("seq = " + repository.getSeq());
System.out.println("size = " + repository.size());
在测试并发时,我发现了一些意想不到的事情。
使用 concurrentHashMap 和 AtomicLong 控制并发。
public class HumanRepository {
private final static Map<Long, Human> STORE = new ConcurrentHashMap<>();
private AtomicLong sequence = new AtomicLong();
public void save(Human human) {
STORE.put(sequence.incrementAndGet(), human);
}
public int size() {
return STORE.size();
}
public Long getSeq() {
return sequence.get();
}
}
我测试了多线程保存。
@Test
void name() throws NoSuchMethodException, InterruptedException {
final int threads = 3_500;
final ExecutorService es = Executors.newFixedThreadPool(threads);
final CountDownLatch count = new CountDownLatch(threads);
final HumanRepository repository = new HumanRepository();
for (int i = 0; i < threads; i++) {
try {
es.execute(() -> repository.save(new Human("aa")));
} finally {
count.countDown();
}
}
count.await();
System.out.println("seq = " + repository.getSeq());
System.out.println("size = " + repository.size());
}
我同时测试了3500个线程。我期望的结果是 seq 和 size 都是 3500。
但有时我会得到 seq=3499,size=3500。 那真是怪了。奇怪的是seq出来的不是3500,虽然size是3500,seq是3499也没意义
不知道为什么map里面的data number和seq不一样,3500出不来
**如果你在count.await();
之后做Thread.sleep(400L);
,奇怪的是,seq的值为3500
您实际上并不是在等待所有任务完成。也就是说,如果你得到3500/3500输出,那是偶然的。
具体来说,您在安排作业后减少主线程上的倒计时闩锁,而不是作业内部,一旦完成.这意味着您的 countdownlatch 基本上只是另一个不进行任何 inter-thread 通信的美化循环变量。尝试这样的事情:
for (int i = 0; i < threads; i++) {
es.execute(() -> {
repository.save(new Human("aa"));
count.countDown();
});
}
您在执行 HumanRepository.save() 的线程外调用 count.countDown()。所以可能主线程没有同步完成线程。
因此您可能会看到 repository.getSeq() 的结果,而一个线程是 运行。您可以尝试使用以下代码吗?
final int threads = 3_500;
final ExecutorService es = Executors.newFixedThreadPool(threads);
final CountDownLatch count = new CountDownLatch(threads);
final HumanRepository repository = new HumanRepository();
for (int i = 0; i < threads; i++) {
try {
es.execute(() -> {
repository.save(new Human("aa"));
count.countDown();
});
} finally {
}
}
count.await();
System.out.println("seq = " + repository.getSeq());
System.out.println("size = " + repository.size());