问题
I\'ve used Java Executors in my multi-threading apps, but I can\'t seem to figure out when is the best to use each of the following ways:
1.
ExecutorService executor=Executors.newFixedThreadPool(50);
executor.execute(new A_Runner(... some parameter ...));
executor.shutdown();
while (!executor.isTerminated()) { Thread.sleep(100); }
2.
int Page_Count=200;
ExecutorService executor=Executors.newFixedThreadPool(50);
doneSignal=new CountDownLatch(Page_Count);
for (int i=0;i<Page_Count;i++) executor.execute(new A_Runner(doneSignal, ... some parameter ...));
doneSignal.await();
executor.shutdown();
while (!executor.isTerminated()) { Thread.sleep(100); }
3.
int Executor_Count=30;
ThreadPoolExecutor executor=new ThreadPoolExecutor(Executor_Count,Executor_Count*2,1,TimeUnit.SECONDS,new LinkedBlockingQueue());
List<Future<String>> futures=new ArrayList<>(3330);
for (int i=0;i<50;i++) futures.add(executor.submit(new A_Runner(... some parameter ...));
executor.shutdown();
while (!executor.isTerminated()) { executor.awaitTermination(1,TimeUnit.SECONDS); }
for (Future<String> future : futures)
{
String f=future.get();
// ...
}
Specifically, in [2] what if I skip the doneSignal, then it\'ll be like [1], so what\'s the use for the doneSignal?
Also, in [3], what if I add a doneSignal? Or is it possible?
What I\'d like to know is : are these approaches interchangeable, or is there a certain situation that I\'m supposed to use a specific type above?
回答1:
ExecutorService
ExecutorService executor=Executors.newFixedThreadPool(50);
It is simple and easy to use. It hides low level details of
ThreadPoolExecutor
.Prefer this one when number of
Callable/Runnable
tasks are small in number and piling of tasks in unbounded queue does not increase memory & degrade the performance of the system. If you haveCPU/Memory
constraints, useThreadPoolExecutor
with capacity constraints &RejectedExecutionHandler
to handle rejection of tasks.CountDownLatch
You have initialized
CountDownLatch
with a given count. This count is decremented by calls to thecountDown()
method. I am assuming that you are calling decrement in your Runnable task later. Threads waiting for this count to reach zero can call one of theawait()
methods. Callingawait()
blocks the thread until the count reaches zero. This class enables a java thread to wait until other set of threads completes their tasks.Use cases:
Achieving Maximum Parallelism: Sometimes we want to start a number of threads at the same time to achieve maximum parallelism
Wait N threads to completes before start execution
Deadlock detection.
Have a look at this article by Lokesh Gupta for more details.
ThreadPoolExecutor : It provides more control to finetune various thread pool parameters. If your application is constrained by number of active
Runnable/Callable
tasks, you should use bounded queue by setting the max capacity. Once the queue reaches maximum capacity, you can define RejectionHandler. Java provides four types ofRejectedExecutionHandler
policies.In the default
ThreadPoolExecutor.AbortPolicy
, the handler throws a runtime RejectedExecutionException upon rejection.In
ThreadPoolExecutor.CallerRunsPolicy
, the thread that invokes execute itself runs the task. This provides a simple feedback control mechanism that will slow down the rate that new tasks are submitted.In
ThreadPoolExecutor.DiscardPolicy
, a task that cannot be executed is simply dropped.In
ThreadPoolExecutor.DiscardOldestPolicy
, if the executor is not shut down, the task at the head of the work queue is dropped, and then execution is retried (which can fail again, causing this to be repeated.)If you want to simulate
CountDownLatch
behaviour, you can useinvokeAll()
method.
One more mechanism you did not quote is ForkJoinPool
The
ForkJoinPool
was added to Java in Java 7. TheForkJoinPool
is similar to the JavaExecutorService
but with one difference. TheForkJoinPool
makes it easy for tasks to split their work up into smaller tasks which are then submitted to theForkJoinPool
too. Task stealing happens inForkJoinPool
when free worker threads steal tasks from busy worker thread queue.Java 8 has introduced one more API in ExecutorService to create work stealing pool. You don't have to create
RecursiveTask
andRecursiveAction
but still can useForkJoinPool
.public static ExecutorService newWorkStealingPool()
Creates a work-stealing thread pool using all available processors as its target parallelism level.
By default, it will take number of CPU cores as parameter.
All these four mechanism are complimentary to each other. Depending on level of granularity you want to control, you have to chose right ones.
来源:https://stackoverflow.com/questions/33596079/how-to-properly-use-java-executor