Executor框架

(已迁移)

--  线程池框架

--  采用线程池好处:

  1、降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

  2、提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

  3、提高线程的可管理性。

--  Executor:执行者,java线程池框架最上层父接口。接口中只有一个execute方法,该方法的作用是向线程池提交任务并执行。

--  ExecutorService:该接口继承自Executor接口,添加了shutdown,shutdownAll,submit,invokeAll等一系列对线程的操作方法。

--  AbstractExecutorService:抽象类,实现ExecutorService接口。

--  ThreadPoolExecutor:java线程池最核心的一个类,继承自AbstractExecutorService,主要功能是创建线程池,给任务分配线程资源,执行任务。

--  ScheduledExecutorService和ScheduledThreadPoolExecotor:延迟执行和周期性执行的线程池。

--  Executors:静态工厂类,该类定义了一系列静态工厂方法,通过这些工厂方法可以返回各种不同的线程池。

 

  --  SingleThreadExecutor

      仅有一个线程,以顺序方式执行任务。如果此线程在执行任务时因异常挂掉,则会创建一个新线程来代替此线程,后续任务将在新线程中执行。

1 ExecutorService executorService = Executors.newSingleThreadExecutor();

 

  --  FixedThreadPool(n)

      拥有固定数量线程的线程池。提交给Executor的任务由固定的n个线程执行,如果有更多的任务,它们会存储在LinkedBlockingQueue里。n通常与底层处理器支持的线程总数有关。

1 ExecutorService executorService = Executors.newFixedThreadPool(4);

 

  --  CachedThreadPool

      主要用于执行大量短期并行任务的场景。与固定线程池不同,此线程池线程数不受限制。如果所有的线程都忙于执行任务且又有新的任务到来,这个线程池将创建一个新的线程并将其提交到Executor。只要其中一个线程变为空闲,他就会执行新的任务。如果一个线程有60s的时间都是空闲的,它们将被结束生命周期并从缓存总被删除。

1 ExecutorService executorService = Executors.newCachedThreadPool();

  

  --  ScheduledExecutor

      当我们有一个需要定期运行的任务,或者我们希望延迟某个任务时,就会使用此类型。

1 ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
1 scheduledExecutorService.scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);
2 scheduledExecutorService.scheduleWithFixedDelay(Runnable command, long initialDelay, long period, TimeUnit unit);
      scheduleAtFixedRate:以固定的频率来执行某项任务。以上一个任务开始的时间计时,period时间过去后,检查上一个任务是否执行完毕,如果上一个任务执行完毕,则当前任务立即执行,如果上一个任务没有执行完毕,则需要等上一个任务执行完毕后,再立即执行。
      scheduleWithFixedDelay:以上一个任务结束时开始计时,period时间过去后,立即执行。
      参数command为具体的执行任务,initialDelay为初始延迟时间(多长时间之后,第一次跑任务),unit为时间单位。
        (参考自:https://blog.csdn.net/u013819945/article/details/47723091)

  
  --  Future对象
      交给Executor的任务是异步的,处理结果由Future对象来接收。
1 Future<String> result = executorService.submit(callableTask);

        调用者可继续执行主程序,当需要提交任务结果时,调用者主程序可调用Future对象的.get()方法来获取。如果任务完成,结果将立即返回给调用者,否则调用者将被阻塞,直到Executor完成此操作的执行并计算出结果。

        Future.get(long timeout, TimeUnit unit):如果在规定时间内没有返回结果,则抛出TimeoutException,调用者可做相应处理。

        如果在执行任务时出现异常,则对get方法的调用将抛出ExecutionException异常。

        另外,只有提交的任务实现了java.util.concurrent.Callable接口时才会返回Future。如果任务实现了Runnable接口,那么一旦任务成功,对.get()方法的调用将返回null。

        Future.cancel(boolean mayInterruptIfRunning)方法用于取消提交任务的执行。如果任务已在执行,则Executor将尝试在mayInterruptIfRunning标志为true时,中断任务执行。

 

--  简单实现

 1 import java.util.concurrent.Callable;
 2 
 3 public class Task implements Callable<String> {
 4     private String message;
 5 
 6     public Task(String message){
 7         this.message = message;
 8     }
 9 
10     @Override
11     public String call() throws Exception{
12         return "Hello " + message + "!";
13     }
14 }
15 
16 
17 import java.util.concurrent.*;
18 
19 public class ApplicationDemo {
20     public static void main(String[] args) {
21         Task task = new Task("World");
22 
23         ExecutorService executorService = Executors.newFixedThreadPool(4);
24         Future<String> result = executorService.submit(task);
25 
26         try{
27             System.out.println(result.get());
28         }catch(InterruptedException | ExecutionException e){
29             System.out.println("Error occured while executing the submitted task");
30             e.printStackTrace();
31         }
32         executorService.shutdown();
33     }
34 }

  打印Hello World!

 

--  合理分配线程池大小:

  对于CPU密集型任务,线程数可为:NCPU+1;

  对于IO密集型任务,线程数可为:2*NCPU。

 

 

--  除了使用Executors工厂类来生成线程池,我们也可以自己配置参数生成个性化的线程池。

 

1 ThreadPoolExecuor tpe = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,timeUnit,runnableTaskQueue,handler);

 

  corePoolSize:核心线程池大小。如果调用了prestarAllCoreThread()方法,那么线程池会提前创建 并启动所有基本线程。

  maximumPoolSize:线程池大小。

  keepAliveTime:线程空闲后,线程存活时间。

  timeUnit:存活时间单位。

  runnableTaskQueue:阻塞队列,可使用ArrayBlockingQueue,LinkBlockingQueue,SynchronousQueue,PriorityBlockingQueue。其中,newFixedThreadPool使用LinkedBlockingQueue,newCachedThreadPool使用SynchronousQueue。

  handler:拒绝策略,当线程池满了时使用。AbortPolicy:默认策略,直接抛出异常;CallerRunsPolicy:只是用调用者所在线程运行任务;DiscardOldestPolicy:丢弃队列中最老任务,执行当前任务;DiscardPolicy:不处理,直接把当前任务丢弃。

 

--  submit()用于提交需要返回值的对象;execute()用于提交无返回值的对象

--  shutdown(),shutdownNow()方法关闭线程池。原理:遍历线程池中线程,逐个调用线程的interrupt()方法来中断线程,所以不响应中断的线程可能无法终止。shutdown中断未执行任务的线程,已执行任务的线程继续执行至结束。shutdownNow尝试中断所有线程,并返回等待执行的任务列表。在调用了shutDown或者shutDownNow后,调用isShutDown()返回true;当所有任务都关闭后,调用isTerminaed()方法返回true。

 

 

--  FixedThreadPool、SingleThreadExecutor、CachedThreadPool是ThreadPoolExecutor的子类。

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

  注:使用LinkedBlockingQueue无限双向队列,线程不拒绝任务。且keepAliveTime无效。

 

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

  注:仅一个线程,其他同上。

 

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

  注:使用SynchronousQueue<Runnable>(),不存储任务元素,实现一对一交付,即每次向线程池put一个任务,必须有线程来take这个任务,否则会一直阻塞该任务。属于无界线程池,可以一直不断创建线程。执行完任务后,空闲线程要么直接接下一个任务,要么保持keepAliveTime的时间之后销毁。

 

 

 

(参考原文:https://blog.csdn.net/tongdanping/article/details/79604637)

(参考原文:https://zhuanlan.zhihu.com/p/94518136)

 

 

 

  1. public static ExecutorService newSingleThreadExecutor() {
  2.  
    return new FinalizableDelegatedExecutorService
  3.  
    (new ThreadPoolExecutor(1, 1,
  4.  
    0L, TimeUnit.MILLISECONDS,
  5.  
    new LinkedBlockingQueue<Runnable>()));
  6.  
    }
posted @ 2020-10-31 23:53  α_伊卡洛斯  阅读(126)  评论(0)    收藏  举报