Java associate object with each thread in Executor

房东的猫 提交于 2021-01-29 02:13:41

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!