问题
I have a bunch of runnables that I want to execute via a thread pool. However, each runnable also writes some result to some file. So right now, the runnable's interface is simple:
class MyRunnable implements Runnable {
...
MyRunnable(BufferedWriter writer, Task t) {
this.writer = writer;
this.task = t;
}
public void run() {
...
this.writer.write(SOME_DATA);
}
}
However, what I want is to associate one BufferedWriter (in other words, one output file) with each of the thread in the Executor pool. However, I'm calling the .execute
function using ExecutorService
like the following:
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);
}
I don't know which thread the executor will assign to run a given task, so I don't know which BufferedWriter I should provide to the runnable. How can I ensure that each thread managed by the ExecutorService is associated with one object (in this case a BufferedWriter)?
回答1:
... 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.
回答2:
There is a class called ThreadLocal for this.
e.g.
ThreadLocal<Type> t = new ThreadLocal<>() {
@Override protected Type initialValue() {
return new Type(Thread.currentThread.getName());
}
This will lazy-initialize a Type
each time a new thread tries to access t
.
The last time I used this class was simply in a class to figure out how many threads a certain machine runs best at. (The answer is usually "the number of cores," but I wanted to make sure that virtual cores drove this number and not physical cores). So I wrote a task in which every thread just spammed an AtomicInteger
counter. But all the threads were fighting over one counter which created a lot of overhead that went into dealing with thread-contention, so I created a threadlocal counter, so the threads could spam their own counters without interference from other threads.
The use cases for it are somewhat obscure since most good multithreaded designs avoid this, but there are times for it of course.
来源:https://stackoverflow.com/questions/19484240/java-associate-object-with-each-thread-in-executor