如何正确使用Java Executor?

12

我在我的多线程应用程序中使用了Java Executors,但我似乎无法弄清楚何时最好使用以下每种方式:

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

具体来说,在[2]中,如果我跳过doneSignal,那么它就像[1]一样,那么doneSignal有什么用呢?

另外,在[3]中,如果我添加一个doneSignal呢?或者这是可能的吗?

我想知道的是:这些方法是否可以互换,还是说在特定情况下我应该使用上述某种类型?


对于那些仍然感到困惑的人,我在阅读了这篇特定的网帖后解决了我的问题。这是链接:http://tutorials.jenkov.com/java-util-concurrent/executorservice.html 很抱歉我知道这个答案太晚了。 - NNAMDI HENRY
1个回答

15
  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 have CPU/Memory constraints, use ThreadPoolExecutor with capacity constraints & RejectedExecutionHandler to handle rejection of tasks.

  2. CountDownLatch

    You have initialized CountDownLatch with a given count. This count is decremented by calls to the countDown() 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 the await() methods. Calling await() 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:

    1. Achieving Maximum Parallelism: Sometimes we want to start a number of threads at the same time to achieve maximum parallelism

    2. Wait N threads to completes before start execution

    3. Deadlock detection.

      Have a look at this article by Lokesh Gupta for more details.

  3. 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 of RejectedExecutionHandler policies.

    1. In the default ThreadPoolExecutor.AbortPolicy, the handler throws a runtime RejectedExecutionException upon rejection.

    2. 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.

    3. In ThreadPoolExecutor.DiscardPolicy, a task that cannot be executed is simply dropped.

    4. 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 use invokeAll() method.

  4. One more mechanism you did not quote is ForkJoinPool

    The ForkJoinPool was added to Java in Java 7. The ForkJoinPool is similar to the Java ExecutorService but with one difference. The ForkJoinPool makes it easy for tasks to split their work up into smaller tasks which are then submitted to the ForkJoinPool too. Task stealing happens in ForkJoinPool 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 and RecursiveAction but still can use ForkJoinPool.

      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.

这四种机制相互补充。根据您想要控制的细粒度级别,您必须选择正确的机制。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接