Java关联对象与Executor中的每个线程

时间:2013-10-20 23:49:26

标签: java multithreading executorservice

我有一堆runnables,我想通过线程池执行。但是,每个runnable也会将一些结果写入某个文件。所以现在,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);
}

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

2 个答案:

答案 0 :(得分:3)

此处有一个名为ThreadLocal的课程。

e.g。

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

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

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

它的用例有点模糊,因为大多数好的多线程设计都避免这种情况,但当然有时候会这样。

答案 1 :(得分:2)

  

...我想要将一个BufferedWriter(换句话说,一个输出文件)与Executor池中的每个线程相关联......

@ djechlin关于ThreadLocal的答案很好,但问题是当线程完成最后一个任务时,你无法访问BufferedWriterclose()

可以在这里看到另一种答案:

  

Threadpool with persistent worker instances

在其中,我建议创建自己的BlockingQueue任务,并为每个线程分配一个任务,让这些线程从队列中获取任务。所以线程运行方法就像:

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();
}

在线程完成时告诉线程在这里有点棘手。你可以在队列中添加一个“毒丸”对象,但是你必须在线程运行时向队列中添加相同数量的对象。