【发布时间】:2017-07-21 20:19:52
【问题描述】:
我刚刚注意到在类的静态初始化期间创建和启动多个线程会导致死锁并且没有线程启动。如果我在类初始化后动态运行相同的代码,这个问题就会消失。这是预期的行为吗?
简短的示例程序:
package com.my.pkg;
import com.google.common.truth.Truth;
import org.junit.Test;
import java.util.Collection;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class MyClass {
private static final Collection<Integer> NUMS = getNums();
@Test
public void fork_doesNotWorkDuringClassInit() {
// This works if you also delete NUMS from above:
// Truth.assertThat(getNums()).containsExactly(0, 1, 2, 3, 4);
Truth.assertThat(NUMS).containsExactly(0, 1, 2, 3, 4);
}
private static Collection<Integer> getNums() {
return IntStream.range(0, 5)
.mapToObj(i -> fork(() -> i))
.map(MyClass::get)
.collect(Collectors.toList());
}
public static <T> FutureTask<T> fork(Callable<T> callable) {
FutureTask<T> futureTask = new FutureTask<>(callable);
Thread thread = new Thread(futureTask);
thread.start();
return futureTask;
}
public static <T> T get(Future<T> future) {
try {
return future.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
【问题讨论】:
-
Thread thread = new Thread(futureTask);==> 不好。Future<Integer> future = executor.submit(callable);==> 好。你不应该手动启动线程,使用executors。 -
不,那不是程序。它甚至不是一个完整的课程。如果您需要故障排除帮助,请提供minimal reproducible example。
-
@alfasin,这是一个教条的答案。使用
ExecutorService是有原因的,创建裸Thread对象也是有原因的。一些反对调用new Thread(...)的论点可以通过调用threadFactory.newThread(...)来处理。 -
我对流的了解并不多,但这看起来像是无操作:
.collect(Collectors.toList()).stream()。我错了,还是这只是将 T 流变成 T 流的一种迂回方式? -
@alfasin 上面的示例程序并不是好的代码。相反,它旨在强调我试图更好地理解的令人惊讶的行为。
标签: java multithreading concurrency thread-safety