1

我有一堆要通过线程池执行的可运行文件。但是,每个可运行文件也会将一些结果写入某个文件。所以现在,runnable 的接口很简单:

class MyRunnable implements Runnable {
    ...
    MyRunnable(BufferedWriter writer, Task t) {
       this.writer = writer;
       this.task = t;
    }

    public void run() {
       ...
       this.writer.write(SOME_DATA);
    }
}

但是,我想要将一个 BufferedWriter(换句话说,一个输出文件)与 Executor 池中的每个线程相关联。但是,我使用如下方式调用该.execute函数:ExecutorService

BufferedWriter[] writers = initialize to 20 BufferedWriters;
ExecutorService executor = Executors.newFixedThreadPool(20);
for (Task t : tasks) {
    MyRunnable runnable = new MyRunnable(WHAT SHOULD GO HERE?, t)
    executor.execute(runnable);
}

我不知道执行程序将分配哪个线程来运行给定任务,所以我不知道应该向可运行对象提供哪个 BufferedWriter。如何确保 ExecutorService 管理的每个线程都与一个对象(在本例中为 BufferedWriter)相关联?

4

2 回答 2

3

有一个班级ThreadLocal为此而呼吁。

例如

ThreadLocal<Type> t = new ThreadLocal<>() {
    @Override protected Type initialValue() {
        return new Type(Thread.currentThread.getName());
}

Type这将在每次新线程尝试访问时延迟初始化 a t

我最后一次使用这个类只是在一个类中计算出某台机器在多少线程上运行得最好。(答案通常是“核心数量”,但我想确保虚拟核心驱动这个数字,而不是物理核心)。所以我写了一个任务,其中每个线程都向一个AtomicInteger计数器发送垃圾邮件。但是所有线程都在争夺一个计数器,这会产生大量用于处理线程争用的开销,因此我创建了一个线程本地计数器,因此线程可以向自己的计数器发送垃圾邮件,而不会受到其他线程的干扰。

它的用例有些模糊,因为大多数优秀的多线程设计都避免了这种情况,但当然也有它的时候。

于 2013-10-20T23:50:50.007 回答
2

... I want is to associate one BufferedWriter (in other words, one output file) with each of the thread in the Executor pool...

@djechlin's answer about ThreadLocal is good but the problem with it is that you can't get access to the BufferedWriter to close() them when the threads finish running the last task.

An alternative answer can be seen here:

Threadpool with persistent worker instances

In it I recommend creating your own BlockingQueue of tasks and forking one task per thread and having those threads get the tasks from your queue. So the thread run method would be something like:

private final BlockingQueue<MyRunnable> queue = new ArrayBlockingQueue<>();
// if you want to shutdown your threads with a boolean
private volatile boolean shutdown;
...

// threads running in the `ExecutorService` will be doing this run() method
public void run() {
    // this allows them to maintain state, in this case your writer
    BufferedWriter writer = ...;
    while (!shutdown && !Thread.currentThread.isInterrupted()) {
        // they get their tasks from your own queue
        MyRunnable runnable = queue.take();
        // if you are using a poison pill but you'll have to add X of them
        if (runnable == STOP_OBJECT) {
            break;
        }
        runnable.run();
    }
    writer.close();
}

Telling the threads when they are done is a little tricky here. You could add a "poison pill" object to the queue but you would have to add the same number of objects to the queue as there are threads running.

于 2013-10-21T00:04:48.260 回答