这是我的情况:我正在研究一些单线程代码。代码是随机的,我需要运行它的许多实例来了解平均值。为了让生活更轻松,更快,我目前的解决方案是使用Executors.newFixedThreadPool
。现在,问题是每个线程都需要拥有自己的独立数据。我最初实现它的方式是这样的:
public class Data {
private static Map<Thread, Data> instanceMap = new ConcurrentHashMap<Thread, Data>();
public static Data getInstance() {
if (!instanceMap.containsKey(Thread.currentThread()))
instanceMap.put(Thread.currentThread(), new Data());
return instanceMap.get(Thread.currentThread());
}
private Data() {
}
}
当迭代次数<=线程数时,这很好。但是,当我使用8个线程运行20次迭代时,就会出现问题。因为将重复使用这8个线程来运行这20个迭代,instanceMap
将只包含8个实例。
那么正确处理这样的事情的方法是什么?我真正需要的是一个线程池,它可以在每次迭代后终止线程。有没有办法杀死run
给Runnable
ExecutorService.submit
对象的newFixedThreadPool
方法中的线程?我应该考虑使用{{1}}的替代方案吗?
答案 0 :(得分:4)
主要问题是您正在存储全局地图中某个线程执行的任务的本地数据。只需将数据范围扩展到任务,一切都会更简单:
Callable<Foo> task = new Callable<Foo>() {
private Data data = new Data();
public Foo call() {
// execute your task here, using the task's data
}
});
executorService.submit(task);
答案 1 :(得分:1)
JB Nizet的答案很好,但考虑到你拥有的,另一种方法可能是将你现有的Runnable
包裹在另一个Runnable
或Callable
内,这会删除Data
个对象原始Runnable
运行后:
class DataRunnable implements Runnable {
private final Runnable child;
DataRunnable(Runnable aChild) { child = aChild; }
@Override
public void run() {
child.run();
Data.removeOldDataForThisThread();
}
}
让我补充一点,使用Data.getInstance()
对象来保存实例可以简化您的ThreadLocal
方法:
public class Data {
private static ThreadLocal<Data> datas = new ThreadLocal<Data>() {
@Override
protected Data initialValue() { return new Data(); }
};
public static Data getInstance() {
return datas.get();
}
public static void removeOldDataForThisThread() { datas.remove(); }
private Data() {
}
}