GVKun编程网logo

如何在不关闭执行器的情况下等待ThreadPoolExecutor中的所有任务完成?(在关闭计算机时选择什么命令可以在不关闭程序的情况下)

15

对于如何在不关闭执行器的情况下等待ThreadPoolExecutor中的所有任务完成?感兴趣的读者,本文将提供您所需要的所有信息,我们将详细讲解在关闭计算机时选择什么命令可以在不关闭程序的情况下,并

对于如何在不关闭执行器的情况下等待ThreadPoolExecutor中的所有任务完成?感兴趣的读者,本文将提供您所需要的所有信息,我们将详细讲解在关闭计算机时选择什么命令可以在不关闭程序的情况下,并且为您提供关于Executors.newFixedThreadPool(1)和Executors.newSingleThreadExecutor()之间的区别、Executor框架(二)Executors、ThreadPoolExecutor以及线程池执行任务的行为方式、Java Thread Pool Example using Executors and ThreadPoolExecutor、java – 一旦我认为它完成,如何在ScheduledThreadPoolExecutor中停止任务的宝贵知识。

本文目录一览:

如何在不关闭执行器的情况下等待ThreadPoolExecutor中的所有任务完成?(在关闭计算机时选择什么命令可以在不关闭程序的情况下)

如何在不关闭执行器的情况下等待ThreadPoolExecutor中的所有任务完成?(在关闭计算机时选择什么命令可以在不关闭程序的情况下)

我不能使用shutdown()awaitTermination()因为有可能在等待时将新任务添加到ThreadPoolExecutor中。

因此,我正在寻找一种方法,等待ThreadPoolExecutor清空队列并完成所有任务,而又不阻止在此之前添加新任务。

如果有什么不同,这适用于Android。

谢谢

更新 :几周后,我再次进行了修改,发现在这种情况下,修改后的CountDownLatch对我而言效果更好。我会保留答案,因为它更适用于我的要求。

答案1

小编典典

如果您想知道何时完成某项任务或某批任务,可以使用ExecutorService.submit(Runnable)。调用此方法将返回一个Future对象,该对象可以放置在Collection其中,然后您的主线程将在调用Future.get()每个对象时进行迭代。这将导致您的主线程停止执行,直到ExecutorService已处理完所有Runnable任务。

Collection<Future<?>> futures = new LinkedList<Future<?>>();futures.add(executorService.submit(myRunnable));for (Future<?> future:futures) {    future.get();}

Executors.newFixedThreadPool(1)和Executors.newSingleThreadExecutor()之间的区别

Executors.newFixedThreadPool(1)和Executors.newSingleThreadExecutor()之间的区别

我的问题是:使用有意义吗Executors.newFixedThreadPool(1)???在两个线程(main +
oneAnotherThread)中,使用执行程序服务效率高吗?是否通过调用new Runnable(){}比使用ExecutorService更好地直接创建新线程?在这种情况下使用ExecutorService有什么好处和坏处?

PS:主线程和oneAnotherThread不访问任何公共资源。

我经历了:使用ExecutorService有什么优势?。一次只能有一个线程!

答案1

小编典典

使用有意义Executors.newFixedThreadPool(1)吗?

它与a本质上是相同的,Executors.newSingleThreadExecutor()不同之处在于后者是不可重新配置的(如javadoc所示),而前者是将其转换为ThreadPoolExecutor

在两个线程(main + oneAnotherThread)中,使用执行程序服务效率高吗?

执行程序服务是围绕线程的非常薄的包装器,可显着促进线程生命周期管理。如果您只需要newThread(runnable).start();继续前进,那么就不需要ExecutorService了。

在任何现实生活中,都有可能监视任务的生命周期(通过返回的Futures),执行者将在未捕获的异常情况下根据需要重新创建线程的事实,回收线程与创建新的解决方案等,使执行者服务成为功能更强大的解决方案,而无需支付额外的费用。

底线:使用执行程序服务和线程相比,我看不到任何不利之处。

Executors.newSingleThreadExecutor()。execute(command)和new
Thread(command).start();之间的区别
经历了两个选项之间行为上的细微差异。

Executor框架(二)Executors、ThreadPoolExecutor以及线程池执行任务的行为方式

Executor框架(二)Executors、ThreadPoolExecutor以及线程池执行任务的行为方式

本文已同步至个人博客liaosi''s blog-Executor框架(二)Executors、ThreadPoolExecutor以及线程池执行任务的行为方式

ThreadPoolExecutor

ThreadPoolExecutor是Executor框架最重要的一个类,它即是真正意义上的线程池。该类的源码有两千多行,但大部分是注释说明,而且还有一些private/protected的方法,真正会用到的方法也并不太多。

先了解一下它的构造器。

ThreadPoolExecutor的构造器

ThreadPoolExecutor的构造器有4个重载的构造器,其中有两个ThreadFactory和RejectedExecutionHandler类型的参数是可选的。最完整的构造器如下:

     /**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     * @param unit the time unit for the {@code keepAliveTime} argument
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     * @param threadFactory the factory to use when the executor
     *        creates a new thread
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached
     * @throws IllegalArgumentException if one of the following holds:<br>
     *         {@code corePoolSize < 0}<br>
     *         {@code keepAliveTime < 0}<br>
     *         {@code maximumPoolSize <= 0}<br>
     *         {@code maximumPoolSize < corePoolSize}
     * @throws NullPointerException if {@code workQueue}
     *         or {@code threadFactory} or {@code handler} is null
     */   
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

构造器的各个参数说明:

  • corePoolSize:核心线程数,核心线程会一直存活,即使没有任务需要处理。但如果设置了allowCoreThreadTimeOut `为 true 则核心线程也会超时退出。
  • maximumPoolSize:最大线程数,线程池中可允许创建的最大线程数。
  • keepAliveTime:当线程池中的线程数大于核心线程数,那些多余的线程空闲时间达到keepAliveTime后就会退出,直到线程数量等于corePoolSize。如果设置了allowCoreThreadTimeout设置为true,则所有线程均会退出直到线程数量为0。
  • unit:keepAliveTime参数的时间单位
  • workQueue:在任务执行前用来保存任务的 阻塞队列。这个队列只会保存通过execute方法提交到线程池的Runnable任务。在ThreadPoolExecutor线程池的API文档中,一共推荐了三种等待队列,它们是:SynchronousQueue、LinkedBlockingQueue 和 ArrayBlockingQueue。
  • threadFactory:线程池创建新线程时使用的factory。默认使用defaultThreadFactory创建线程。
  • handle:饱和策略。当线程池的线程数已达到最大,并且任务队列已满时来处理被拒绝任务的策略。默认使用ThreadPoolExecutor.AbortPolicy,任务被拒绝时将抛出RejectExecutorException

除此之外,ThreadPoolExecutor还有两个个常用的参数设置:

  • allowCoreThreadTimeout:是否允许核心线程空闲退出,默认值为false。
  • queueCapacity:任务队列的容量。

ThreadPoolExecutor线程池的逻辑结构图:
图片描述

线程池执行任务的行为方式

线程池按以下行为执行任务

1. 当线程数小于核心线程数时,创建线程。
2. 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
3. 当线程数大于等于核心线程数,且任务队列已满
    1. 若线程数小于最大线程数,创建线程
    2. 若线程数等于最大线程数,抛出异常,拒绝任务

图片描述

Executors

Executors类是一个工厂类,提供了一系列静态工厂方法来创建不同的ExecutorService或 ScheduledExecutorService实例。

创建3种不同的ExecutorService(线程池)实例

1.newSingleThreadExecutor

创建一个单线程的线程池:启动一个线程负责按顺序执行任务,先提交的任务先执行。

其原理是:任务会被提交到一个队列里,启动的那个线程会从队里里取任务,然后执行,执行完,再从队列里取下一个任务,再执行。如果该线程执行一个任务失败,并导致线程结束,系统会创建一个新的线程去执行队列里后续的任务,不会因为前面的任务有异常导致后面无辜的任务无法执行。
源码:

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

2.newFixedThreadPool

创建一个可重用的固定线程数量的线程池。即corePoolSize=线程池中的线程数= maximumPoolSize。

  • 如果没有任务执行,所有的线程都将等待。
  • 如果线程池中的所有线程都处于活动状态,此时再提交任务就在队列中等待,直到有可用线程。
  • 如果线程池中的某个线程由于异常而结束时,线程池就会再补充一条新线程。

源码:

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

3.newCachedThreadPool

创建一个不限制线程数量的动态线程池。

  • 因为有多个线程存在,任务不一定会按照顺序执行。
  • 一个线程完成任务后,空闲时间达到60秒则会被结束。
  • 在执行新的任务时,当线程池中有之前创建的空闲线程就使用这个线程,否则就新建一条线程。

源码:

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

可以看到newCachedThreadPool使用的队列是SynchronousQueue,和前两个是不一样的。线程池的线程数可达到Integer.MAX_VALUE,即2147483647。此外由于会有线程的创建和销毁,所以会有一定的系统开销。

4.newSingleThreadExecutor 与 newFixedThreadPool(1) 的区别

JavaDoc上说:

Unlike the otherwise equivalent newFixedThreadPool(1) the returned executor is guaranteed not to be reconfigurable to use additional threads.

举个例子:

((ThreadPoolExecutor)newFixedThreadPool(1)).setCorePoolSize(3);

newFixedThreadPool(1)可以后期修改线程数,不保证线程只有一个。而newSingleThreadExecutor可以保证。

创建ScheduledExecutorService实例

关于ScheduledExecutorService的内容,在下一篇文章中介绍。

1.newSingleThreadScheduledExecutor

创建一个单线程的ScheduledExecutorService,在指定延时之后执行或者以固定的频率周期性的执行提交的任务。在线程池关闭之前如果有一个任务执行失败,并导致线程结束,系统会创建一个新的线程接着执行队列里的任务。

源码:

    public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1));   //corePoolSize为1
    }

还有一个重载的方法,多了一个ThreadFactory参数,ThreadFactory是用来确定新线程应该怎么创建的。

    public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1, threadFactory));
    }

2.newScheduledThreadPool

创建一个固定线程数的ScheduledExecutorService对象,在指定延时之后执行或者以固定的频率周期性的执行提交的任务。

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

同样的,也有一个重载的方法:

    public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
    }

Java Thread Pool Example using Executors and ThreadPoolExecutor

Java Thread Pool Example using Executors and ThreadPoolExecutor

A thread pool manages the pool of worker threads, it contains a queue that keeps tasks waiting to get executed. A thread pool manages the collection of Runnable threads and worker threads execute Runnable from the queue. java.util.concurrent.Executors  provide implementation of java.util.concurrent.Executor  interface to create the thread pool in java.
Let’s write a simple program to explain it’s working.
 

First we need to have a Runnable class.

01 package com.journaldev.threadpool;
02  
03 public class WorkerThread implements Runnable {
04  
05     private String command;
06  
07     public WorkerThread(String s){
08         this.command=s;
09     }
10  
11     @Override
12     public void run() {
13         System.out.println(Thread.currentThread().getName()+'' Start. Command = ''+command);
14         processCommand();
15         System.out.println(Thread.currentThread().getName()+'' End.'');
16     }
17  
18     private void processCommand() {
19         try {
20             Thread.sleep(5000);
21         } catch (InterruptedException e) {
22             e.printStackTrace();
23         }
24     }
25  
26     @Override
27     public String toString(){
28         return this.command;
29     }
30 }

Here is the test program where we are creating fixed thread pool from Executors framework.

01 package com.journaldev.threadpool;
02  
03 import java.util.concurrent.ExecutorService;
04 import java.util.concurrent.Executors;
05  
06 public class SimpleThreadPool {
07  
08     public static void main(String[] args) {
09         ExecutorService executor = Executors.newFixedThreadPool(5);
10         for (int i = 0; i < 10; i++) {
11             Runnable worker = new WorkerThread('''' + i);
12             executor.execute(worker);
13           }
14         executor.shutdown();
15         while (!executor.isTerminated()) {
16         }
17         System.out.println(''Finished all threads'');
18     }
19  
20 }

In above program, we are creating fixed size thread pool of 5 worker threads. Then we are submitting 10 jobs to this pool, since the pool size is 5, it will start working on 5 jobs and other jobs will be in wait state, as soon as one of the job is finished, another job from the wait queue will be picked up by worker thread and get’s executed.

Here is the output of the above program.

01 pool-1-thread-2 Start. Command = 1
02 pool-1-thread-4 Start. Command = 3
03 pool-1-thread-1 Start. Command = 0
04 pool-1-thread-3 Start. Command = 2
05 pool-1-thread-5 Start. Command = 4
06 pool-1-thread-4 End.
07 pool-1-thread-5 End.
08 pool-1-thread-1 End.
09 pool-1-thread-3 End.
10 pool-1-thread-3 Start. Command = 8
11 pool-1-thread-2 End.
12 pool-1-thread-2 Start. Command = 9
13 pool-1-thread-1 Start. Command = 7
14 pool-1-thread-5 Start. Command = 6
15 pool-1-thread-4 Start. Command = 5
16 pool-1-thread-2 End.
17 pool-1-thread-4 End.
18 pool-1-thread-3 End.
19 pool-1-thread-5 End.
20 pool-1-thread-1 End.
21 Finished all threads

The output confirms that there are five threads in the pool named from “pool-1-thread-1? to “pool-1-thread-5? and they are responsible to execute the submitted tasks to the pool.

Executors class provide simple implementation of ExecutorService using ThreadPoolExecutor but ThreadPoolExecutor provides much more feature than that. We can specify the number of threads that will be alive when we create ThreadPoolExecutor instance and we can limit the size of thread pool and create our own RejectedExecutionHandler implementation to handle the jobs that can’t fit in the worker queue.

Here is our custom implementation of RejectedExecutionHandler interface.

01 package com.journaldev.threadpool;
02  
03 import java.util.concurrent.RejectedExecutionHandler;
04 import java.util.concurrent.ThreadPoolExecutor;
05  
06 public class RejectedExecutionHandlerImpl implements RejectedExecutionHandler {
07  
08     @Override
09     public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
10         System.out.println(r.toString() + '' is rejected'');
11     }
12  
13 }

ThreadPoolExecutor provides several methods using which we can find out the current state of executor, pool size, active thread count and task count. So I have a monitor thread that will print the executor information at certain time interval.

01 package com.journaldev.threadpool;
02  
03 import java.util.concurrent.ThreadPoolExecutor;
04  
05 public class MyMonitorThread implements Runnable
06 {
07     private ThreadPoolExecutor executor;
08  
09     private int seconds;
10  
11     private boolean run=true;
12  
13     public MyMonitorThread(ThreadPoolExecutor executor, int delay)
14     {
15         this.executor = executor;
16         this.seconds=delay;
17     }
18  
19     public void shutdown(){
20         this.run=false;
21     }
22  
23     @Override
24     public void run()
25     {
26         while(run){
27                 System.out.println(
28                     String.format(''[monitor] [%d/%d] Active: %d, Completed: %d, Task: %d, isShutdown: %s, isTerminated: %s'',
29                         this.executor.getPoolSize(),
30                         this.executor.getCorePoolSize(),
31                         this.executor.getActiveCount(),
32                         this.executor.getCompletedTaskCount(),
33                         this.executor.getTaskCount(),
34                         this.executor.isShutdown(),
35                         this.executor.isTerminated()));
36                 try {
37                     Thread.sleep(seconds*1000);
38                 } catch (InterruptedException e) {
39                     e.printStackTrace();
40                 }
41         }
42  
43     }
44 }

Here is the thread pool implementation example using ThreadPoolExecutor.

01 package com.journaldev.threadpool;
02  
03 import java.util.concurrent.ArrayBlockingQueue;
04 import java.util.concurrent.Executors;
05 import java.util.concurrent.ThreadFactory;
06 import java.util.concurrent.ThreadPoolExecutor;
07 import java.util.concurrent.TimeUnit;
08  
09 public class WorkerPool {
10  
11     public static void main(String args[]) throws InterruptedException{
12         //RejectedExecutionHandler implementation
13         RejectedExecutionHandlerImpl rejectionHandler = new RejectedExecutionHandlerImpl();
14         //Get the ThreadFactory implementation to use
15         ThreadFactory threadFactory = Executors.defaultThreadFactory();
16         //creating the ThreadPoolExecutor
17         ThreadPoolExecutor executorPool = new ThreadPoolExecutor(2, 4, 10, TimeUnit.SECONDS, newArrayBlockingQueue<Runnable>(2), threadFactory, rejectionHandler);
18         //start the monitoring thread
19         MyMonitorThread monitor = new MyMonitorThread(executorPool, 3);
20         Thread monitorThread = new Thread(monitor);
21         monitorThread.start();
22         //submit work to the thread pool
23         for(int i=0; i<10; i++){
24             executorPool.execute(new WorkerThread(''cmd''+i));
25         }
26  
27         Thread.sleep(30000);
28         //shut down the pool
29         executorPool.shutdown();
30         //shut down the monitor thread
31         Thread.sleep(5000);
32         monitor.shutdown();
33  
34     }
35 }

Notice that while initializing the ThreadPoolExecutor, we are keeping initial pool size as 2, maximum pool size to 4 and work queue size as 2. So if there are 4 running tasks and more tasks are submitted, the work queue will hold only 2 of them and rest of them will be handled by RejectedExecutionHandlerImpl.

Here is the output of above program that confirms above statement.

01 pool-1-thread-1 Start. Command = cmd0
02 pool-1-thread-4 Start. Command = cmd5
03 cmd6 is rejected
04 pool-1-thread-3 Start. Command = cmd4
05 pool-1-thread-2 Start. Command = cmd1
06 cmd7 is rejected
07 cmd8 is rejected
08 cmd9 is rejected
09 [monitor] [0/2] Active: 4, Completed: 0, Task: 6, isShutdown: false, isTerminated: false
10 [monitor] [4/2] Active: 4, Completed: 0, Task: 6, isShutdown: false, isTerminated: false
11 pool-1-thread-4 End.
12 pool-1-thread-1 End.
13 pool-1-thread-2 End.
14 pool-1-thread-3 End.
15 pool-1-thread-1 Start. Command = cmd3
16 pool-1-thread-4 Start. Command = cmd2
17 [monitor] [4/2] Active: 2, Completed: 4, Task: 6, isShutdown: false, isTerminated: false
18 [monitor] [4/2] Active: 2, Completed: 4, Task: 6, isShutdown: false, isTerminated: false
19 pool-1-thread-1 End.
20 pool-1-thread-4 End.
21 [monitor] [4/2] Active: 0, Completed: 6, Task: 6, isShutdown: false, isTerminated: false
22 [monitor] [2/2] Active: 0, Completed: 6, Task: 6, isShutdown: false, isTerminated: false
23 [monitor] [2/2] Active: 0, Completed: 6, Task: 6, isShutdown: false, isTerminated: false
24 [monitor] [2/2] Active: 0, Completed: 6, Task: 6, isShutdown: false, isTerminated: false
25 [monitor] [2/2] Active: 0, Completed: 6, Task: 6, isShutdown: false, isTerminated: false
26 [monitor] [2/2] Active: 0, Completed: 6, Task: 6, isShutdown: false, isTerminated: false
27 [monitor] [0/2] Active: 0, Completed: 6, Task: 6, isShutdown: true, isTerminated: true
28 [monitor] [0/2] Active: 0, Completed: 6, Task: 6, isShutdown: true, isTerminated: true

Notice the change in active, completed and total completed task count of the executor. We can invoke shutdown() method to finish execution of all the submitted tasks and terminate the thread pool.
 

Reference: Java Thread Pool Example using Executors and ThreadPoolExecutor from our JCG partner Pankaj Kumar at the Developer Recipes blog.


java – 一旦我认为它完成,如何在ScheduledThreadPoolExecutor中停止任务

java – 一旦我认为它完成,如何在ScheduledThreadPoolExecutor中停止任务

我有一个ScheduledThreadPoolExecutor,我计划任务以固定的速率运行.我希望任务以指定的延迟运行,最多为10次,直到“成功”为止.之后,我不想让任务重试.所以基本上我需要停止运行计划的任务,当我想要停止它,但不关闭ScheduledThreadPoolExecutor.任何想法我该怎么做?

这里有一些伪代码 –

public class ScheduledThreadPoolExecutorTest
{
  public static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(15);  // no multiple instances,just one to serve all requests

  class MyTask implements Runnable
  {
    private int MAX_ATTEMPTS = 10;
    public void run()
    {
      if(++attempt <= MAX_ATTEMPTS)
      {
        doX();
        if(doXSucceeded)
        {
          //stop retrying the task anymore
        }
      }
      else
      { 
        //Couldn't succeed in MAX attempts,don't bother retrying anymore!
      }
    }
  }

  public void main(String[] args)
  {
    executor.scheduleAtFixedrate(new ScheduledThreadPoolExecutortest().new MyTask(),5,TimeUnit.SECONDS);
  }
}

解决方法

运行此测试,打印1 2 3 4 5并停止
public class ScheduledThreadPoolExecutorTest {
    static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(15); // no
    static ScheduledFuture<?> t;

    static class MyTask implements Runnable {
        private int attempt = 1;

        public void run() {
            System.out.print(attempt + " ");
            if (++attempt > 5) {
                t.cancel(false);
            }
        }
    }

    public static void main(String[] args) {
        t = executor.scheduleAtFixedrate(new MyTask(),1,TimeUnit.SECONDS);
    }
}

关于如何在不关闭执行器的情况下等待ThreadPoolExecutor中的所有任务完成?在关闭计算机时选择什么命令可以在不关闭程序的情况下的介绍已经告一段落,感谢您的耐心阅读,如果想了解更多关于Executors.newFixedThreadPool(1)和Executors.newSingleThreadExecutor()之间的区别、Executor框架(二)Executors、ThreadPoolExecutor以及线程池执行任务的行为方式、Java Thread Pool Example using Executors and ThreadPoolExecutor、java – 一旦我认为它完成,如何在ScheduledThreadPoolExecutor中停止任务的相关信息,请在本站寻找。

本文标签: