我有一堆可运行对象,我想通过线程池执行。但是,每个可运行对象也会将一些结果写入某个文件。所以现在,可运行的界面很简单:
class MyRunnable implements Runnable {
...
MyRunnable(BufferedWriter writer, Task t) {
this.writer = writer;
this.task = t;
}
public void run() {
...
this.writer.write(SOME_DATA);
}
}
但是,我想要的是将一个 BufferedWriter(换句话说,一个输出文件)与执行器池中的每个线程相关联。但是,我使用如下所示ExecutorService
调用.execute
函数:
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)相关联?
为此有一个名为 ThreadLocal
的类。
例如
ThreadLocal<Type> t = new ThreadLocal<>() {
@Override protected Type initialValue() {
return new Type(Thread.currentThread.getName());
}
这将在每次新线程尝试访问t
时延迟初始化Type
。
我最后一次使用这个类只是在一个类中计算某台机器运行最好的线程数。 (答案通常是"内核数量",但我想确保虚拟内核驱动这个数字,而不是物理内核)。 所以我写了一个任务,其中每个线程都只是向AtomicInteger
计数器发送垃圾邮件。 但是所有线程都在争夺一个计数器,这在处理线程争用时产生了大量开销,因此我创建了一个 threadlocal 计数器,这样线程就可以向自己的计数器发送垃圾邮件,而不会受到其他线程的干扰。
它的用例有些晦涩难懂,因为大多数好的多线程设计都避免了这一点,但当然也有的时候。
。我想要将一个缓冲编写器(换句话说,一个输出文件)与执行器池中的每个线程相关联......
@djechlin关于ThreadLocal
的回答很好,但它的问题在于,当线程完成运行最后一个任务时,您无法访问BufferedWriter
来close()
它们。
可以在这里看到另一个答案:
具有持久工作线程实例的线程池
在其中,我建议创建自己的任务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();
}
在这里告诉线程何时完成有点棘手。 您可以将"毒丸"对象添加到队列中,但您必须向队列中添加与正在运行的线程相同数量的对象。