0

这是我的情况:我正在处理一些单线程代码。代码是随机的,我需要运行它的许多实例来了解平均值。为了让生活更轻松、更快捷,我目前的解决方案是使用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方法中的线程?我应该考虑一些替代方案吗?RunnableExecutorService.submitnewFixedThreadPool

4

2 回答 2

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);
于 2013-08-14T17:40:01.783 回答
1

JB Nizet 的回答很好,但是考虑到您所拥有的,另一种方法可能是将您现有的包装Runnable在另一个内部,Runnable或者在原始运行后Callable删除该对象:DataRunnable

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() {
    }
}
于 2013-08-14T20:50:01.593 回答