Java 线程池2

使用线程池可以给我们带来很多好处,首先通过线程池中线程的重用,减少创建和销毁线程的性能开销。其次,能控制线程池中的并发数,否则会因为大量的线程争夺CPU资源造成阻塞。最后,线程池能够对线程进行管理,比如使用ScheduledThreadPool来设置延迟N秒后执行任务,并且每隔M秒循环执行一次。

下面会通过介绍线程池中的真正实现者——ThreadPoolExecutor来引出Android中的4类线程池的使用以及特性分析,会加上笔者自己的理解,和自认为比较恰当的比喻,帮助理解。

 

1.凡事得靠ThreadPoolExecutor(铺垫环节,懂的直接跳过)

Executor作为一个接口,它的具体实现就是ThreadPoolExecutor

Android中的线程池都是直接或间接通过配置ThreadPoolExecutor来实现不同特性的线程池。

先介绍ThreadPoolExecutor的一个常用的构造方法。

[java]  view plain  copy
  1. /* 
  2. *@ ThreadPoolExecutor构造参数介绍 
  3. *@author SEU_Calvin 
  4. * @date 2016/09/03 
  5. */  
  6. public ThreadPoolExecutor(  
  7. //核心线程数,除非allowCoreThreadTimeOut被设置为true,否则它闲着也不会死  
  8. int corePoolSize,   
  9. //最大线程数,活动线程数量超过它,后续任务就会排队                     
  10. int maximumPoolSize,   
  11. //超时时长,作用于非核心线程(allowCoreThreadTimeOut被设置为true时也会同时作用于核心线程),闲置超时便被回收             
  12. long keepAliveTime,                            
  13. //枚举类型,设置keepAliveTime的单位,有TimeUnit.MILLISECONDS(ms)、TimeUnit. SECONDS(s)等  
  14. TimeUnit unit,  
  15. //缓冲任务队列,线程池的execute方法会将Runnable对象存储起来  
  16. BlockingQueue<Runnable> workQueue,  
  17. //线程工厂接口,只有一个new Thread(Runnable r)方法,可为线程池创建新线程  
  18. ThreadFactory threadFactory)  

ThreadPoolExecutor的各个参数所代表的特性注释中已经写的很清楚了,那么ThreadPoolExecutor执行任务时的心路历程是什么样的呢?(以下用currentSize表示线程池中当前线程数量)

(1)当currentSize<corePoolSize时,没什么好说的,直接启动一个核心线程并执行任务。

(2)当currentSize>=corePoolSize、并且workQueue未满时,添加进来的任务会被安排到workQueue中等待执行。

(3)当workQueue已满,但是currentSize<maximumPoolSize时,会立即开启一个非核心线程来执行任务。

(4)当currentSize>=corePoolSize、workQueue已满、并且currentSize>maximumPoolSize,调用handler默认抛出RejectExecutionExpection异常。


2 Android中的四类线程池

Android中最常见的四类具有不同特性的线程池分别为FixThreadPool、CachedThreadPool、ScheduleThreadPool以及SingleThreadExecutor。

 

2.1     FixThreadPool(一堆人排队上公厕)

[java]  view plain  copy
  1. /* 
  2. *@FixThreadPool介绍 
  3. *@author SEU_Calvin 
  4. * @date 2016/09/03 
  5. */  
  6. public static ExecutorService newFixThreadPool(int nThreads){  
  7.     return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());  
  8. }  
  9. //使用  
  10. Executors.newFixThreadPool(5).execute(r);  

(1)从配置参数来看,FixThreadPool只有核心线程,并且数量固定的,也不会被回收,所有线程都活动时,因为队列没有限制大小,新任务会等待执行。

(2)【前方高能,笔者脑洞】FixThreadPool其实就像一堆人排队上公厕一样,可以无数多人排队,但是厕所位置就那么多,而且没人上时,厕所也不会被拆迁,哈哈o(∩_∩)o ,很形象吧。

(3)由于线程不会回收,FixThreadPool会更快地响应外界请求,这也很容易理解,就好像有人突然想上厕所,公厕不是现用现建的。


2.2     SingleThreadPool(公厕里只有一个坑位)

[java]  view plain  copy
  1. /* 
  2. *@SingleThreadPool介绍 
  3. *@author SEU_Calvin 
  4. * @date 2016/09/03 
  5. */  
  6. public static ExecutorService newSingleThreadPool (int nThreads){  
  7.     return new FinalizableDelegatedExecutorService ( new ThreadPoolExecutor (110, TimeUnit. MILLISECONDS, new LinkedBlockingQueue<Runnable>()) );  
  8. }  
  9. //使用  
  10. Executors.newSingleThreadPool ().execute(r);  

(1)从配置参数可以看出,SingleThreadPool只有一个核心线程,确保所有任务都在同一线程中按顺序完成。因此不需要处理线程同步的问题。

(2)【前方高能,笔者脑洞】可以把SingleThreadPool简单的理解为FixThreadPool的参数被手动设置为1的情况,即Executors.newFixThreadPool(1).execute(r)。所以SingleThreadPool可以理解为公厕里只有一个坑位,先来先上。为什么只有一个坑位呢,因为这个公厕是收费的,收费的大爷上年纪了,只能管理一个坑位,多了就管不过来了(线程同步问题)。


2.3     CachedThreadPool(一堆人去一家很大的咖啡馆喝咖啡)

[java]  view plain  copy
  1. /* 
  2. *@CachedThreadPool介绍 
  3. *@author SEU_Calvin 
  4. * @date 2016/09/03 
  5. */  
  6. public static ExecutorService newCachedThreadPool(int nThreads){  
  7.     return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit. SECONDS, new SynchronousQueue<Runnable>());  
  8. }  
  9. //使用  
  10. Executors.newCachedThreadPool().execute(r);  

(1)CachedThreadPool只有非核心线程,最大线程数非常大,所有线程都活动时,会为新任务创建新线程,否则利用空闲线程(60s空闲时间,过了就会被回收,所以线程池中有0个线程的可能)处理任务。

(2)任务队列SynchronousQueue相当于一个空集合,导致任何任务都会被立即执行

(3)【前方高能,笔者脑洞】CachedThreadPool就像是一堆人去一个很大的咖啡馆喝咖啡,里面服务员也很多,随时去,随时都可以喝到咖啡。但是为了响应国家的“光盘行动”,一个人喝剩下的咖啡会被保留60秒,供新来的客人使用,哈哈哈哈哈,好恶心啊。如果你运气好,没有剩下的咖啡,你会得到一杯新咖啡。但是以前客人剩下的咖啡超过60秒,就变质了,会被服务员回收掉。

(4)比较适合执行大量的耗时较少的任务。喝咖啡人挺多的,喝的时间也不长。


2.4    ScheduledThreadPool4个里面唯一一个有延迟执行和周期重复执行的线程池)

[java]  view plain  copy
  1. /* 
  2. *@ScheduledThreadPool介绍 
  3. *@author SEU_Calvin 
  4. * @date 2016/09/03 
  5. */  
  6. public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize){  
  7. return new ScheduledThreadPoolExecutor(corePoolSize);  
  8. }  
  9. public ScheduledThreadPoolExecutor(int corePoolSize){  
  10. super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedQueue ());  
  11. }  
  12. //使用,延迟1秒执行,每隔2秒执行一次Runnable r  
  13. Executors. newScheduledThreadPool (5).scheduleAtFixedRate(r, 10002000, TimeUnit.MILLISECONDS);  

(1)核心线程数固定,非核心线程(闲着没活干会被立即回收)数没有限制。

(2)从上面代码也可以看出,ScheduledThreadPool主要用于执行定时任务以及有固定周期的重复任务




1、线程池简介:
    多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。    
    假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间。

    如果:T1 + T3 远大于 T2,则可以采用线程池,以提高服务器性能。
                一个线程池包括以下四个基本组成部分:
                1、线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
                2、工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
                3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
                4、任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。
                
    线程池技术正是关注如何缩短或调整T1,T3时间的技术,从而提高服务器程序性能的。它把T1,T3分别安排在服务器程序的启动和结束的时间段或者一些空闲的时间段,这样在服务器程序处理客户请求时,不会有T1,T3的开销了。
    线程池不仅调整T1,T3产生的时间段,而且它还显著减少了创建线程的数目,看一个例子:
    假设一个服务器一天要处理50000个请求,并且每个请求需要一个单独的线程完成。在线程池中,线程数一般是固定的,所以产生线程总数不会超过线程池中线程的数目,而如果服务器不利用线程池来处理这些请求则线程总数为50000。一般线程池大小是远小于50000。所以利用线程池的服务器程序不会为了创建50000而在处理请求时浪费时间,从而提高效率。

    代码实现中并没有实现任务接口,而是把Runnable对象加入到线程池管理器(ThreadPool),然后剩下的事情就由线程池管理器(ThreadPool)来完成了

 

[java]  view plain  copy
  1. package mine.util.thread;  
  2.   
  3. import java.util.LinkedList;  
  4. import java.util.List;  
  5.   
  6. /** 
  7.  * 线程池类,线程管理器:创建线程,执行任务,销毁线程,获取线程基本信息 
  8.  */  
  9. public final class ThreadPool {  
  10.     // 线程池中默认线程的个数为5  
  11.     private static int worker_num = 5;  
  12.     // 工作线程  
  13.     private WorkThread[] workThrads;  
  14.     // 未处理的任务  
  15.     private static volatile int finished_task = 0;  
  16.     // 任务队列,作为一个缓冲,List线程不安全  
  17.     private List<Runnable> taskQueue = new LinkedList<Runnable>();  
  18.     private static ThreadPool threadPool;  
  19.   
  20.     // 创建具有默认线程个数的线程池  
  21.     private ThreadPool() {  
  22.         this(5);  
  23.     }  
  24.   
  25.     // 创建线程池,worker_num为线程池中工作线程的个数  
  26.     private ThreadPool(int worker_num) {  
  27.         ThreadPool.worker_num = worker_num;  
  28.         workThrads = new WorkThread[worker_num];  
  29.         for (int i = 0; i < worker_num; i++) {  
  30.             workThrads[i] = new WorkThread();  
  31.             workThrads[i].start();// 开启线程池中的线程  
  32.         }  
  33.     }  
  34.   
  35.     // 单态模式,获得一个默认线程个数的线程池  
  36.     public static ThreadPool getThreadPool() {  
  37.         return getThreadPool(ThreadPool.worker_num);  
  38.     }  
  39.   
  40.     // 单态模式,获得一个指定线程个数的线程池,worker_num(>0)为线程池中工作线程的个数  
  41.     // worker_num<=0创建默认的工作线程个数  
  42.     public static ThreadPool getThreadPool(int worker_num1) {  
  43.         if (worker_num1 <= 0)  
  44.             worker_num1 = ThreadPool.worker_num;  
  45.         if (threadPool == null)  
  46.             threadPool = new ThreadPool(worker_num1);  
  47.         return threadPool;  
  48.     }  
  49.   
  50.     // 执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管理器觉定  
  51.     public void execute(Runnable task) {  
  52.         synchronized (taskQueue) {  
  53.             taskQueue.add(task);  
  54.             taskQueue.notify();  
  55.         }  
  56.     }  
  57.   
  58.     // 批量执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管理器觉定  
  59.     public void execute(Runnable[] task) {  
  60.         synchronized (taskQueue) {  
  61.             for (Runnable t : task)  
  62.                 taskQueue.add(t);  
  63.             taskQueue.notify();  
  64.         }  
  65.     }  
  66.   
  67.     // 批量执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管理器觉定  
  68.     public void execute(List<Runnable> task) {  
  69.         synchronized (taskQueue) {  
  70.             for (Runnable t : task)  
  71.                 taskQueue.add(t);  
  72.             taskQueue.notify();  
  73.         }  
  74.     }  
  75.   
  76.     // 销毁线程池,该方法保证在所有任务都完成的情况下才销毁所有线程,否则等待任务完成才销毁  
  77.     public void destroy() {  
  78.         while (!taskQueue.isEmpty()) {// 如果还有任务没执行完成,就先睡会吧  
  79.             try {  
  80.                 Thread.sleep(10);  
  81.             } catch (InterruptedException e) {  
  82.                 e.printStackTrace();  
  83.             }  
  84.         }  
  85.         // 工作线程停止工作,且置为null  
  86.         for (int i = 0; i < worker_num; i++) {  
  87.             workThrads[i].stopWorker();  
  88.             workThrads[i] = null;  
  89.         }  
  90.         threadPool=null;  
  91.         taskQueue.clear();// 清空任务队列  
  92.     }  
  93.   
  94.     // 返回工作线程的个数  
  95.     public int getWorkThreadNumber() {  
  96.         return worker_num;  
  97.     }  
  98.   
  99.     // 返回已完成任务的个数,这里的已完成是只出了任务队列的任务个数,可能该任务并没有实际执行完成  
  100.     public int getFinishedTasknumber() {  
  101.         return finished_task;  
  102.     }  
  103.   
  104.     // 返回任务队列的长度,即还没处理的任务个数  
  105.     public int getWaitTasknumber() {  
  106.         return taskQueue.size();  
  107.     }  
  108.   
  109.     // 覆盖toString方法,返回线程池信息:工作线程个数和已完成任务个数  
  110.     @Override  
  111.     public String toString() {  
  112.         return "WorkThread number:" + worker_num + "  finished task number:"  
  113.                 + finished_task + "  wait task number:" + getWaitTasknumber();  
  114.     }  
  115.   
  116.     /** 
  117.      * 内部类,工作线程 
  118.      */  
  119.     private class WorkThread extends Thread {  
  120.         // 该工作线程是否有效,用于结束该工作线程  
  121.         private boolean isRunning = true;  
  122.   
  123.         /* 
  124.          * 关键所在啊,如果任务队列不空,则取出任务执行,若任务队列空,则等待 
  125.          */  
  126.         @Override  
  127.         public void run() {  
  128.             Runnable r = null;  
  129.             while (isRunning) {// 注意,若线程无效则自然结束run方法,该线程就没用了  
  130.                 synchronized (taskQueue) {  
  131.                     while (isRunning && taskQueue.isEmpty()) {// 队列为空  
  132.                         try {  
  133.                             taskQueue.wait(20);  
  134.                         } catch (InterruptedException e) {  
  135.                             e.printStackTrace();  
  136.                         }  
  137.                     }  
  138.                     if (!taskQueue.isEmpty())  
  139.                         r = taskQueue.remove(0);// 取出任务  
  140.                 }  
  141.                 if (r != null) {  
  142.                     r.run();// 执行任务  
  143.                 }  
  144.                 finished_task++;  
  145.                 r = null;  
  146.             }  
  147.         }  
  148.   
  149.         // 停止工作,让该线程自然执行完run方法,自然结束  
  150.         public void stopWorker() {  
  151.             isRunning = false;  
  152.         }  
  153.     }  
  154. }  

 

测试代码:

[java]  view plain  copy
  1. package mine.util.thread;  
  2.   
  3. //测试线程池  
  4. public class TestThreadPool {  
  5.     public static void main(String[] args) {  
  6.         // 创建3个线程的线程池  
  7.         ThreadPool t = ThreadPool.getThreadPool(3);  
  8.         t.execute(new Runnable[] { new Task(), new Task(), new Task() });  
  9.         t.execute(new Runnable[] { new Task(), new Task(), new Task() });  
  10.         System.out.println(t);  
  11.         t.destroy();// 所有线程都执行完成才destory  
  12.         System.out.println(t);  
  13.     }  
  14.   
  15.     // 任务类  
  16.     static class Task implements Runnable {  
  17.         private static volatile int i = 1;  
  18.   
  19.         @Override  
  20.         public void run() {// 执行任务  
  21.             System.out.println("任务 " + (i++) + " 完成");  
  22.         }  
  23.     }  
  24. }  


 

运行结果:

WorkThread number:3  finished task number:0  wait task number:6
任务 1 完成
任务 2 完成
任务 3 完成
任务 4 完成
任务 5 完成
任务 6 完成
WorkThread number:3  finished task number:6  wait task number:0

分析:由于并没有任务接口,传入的可以是自定义的任何任务,所以线程池并不能准确的判断该任务是否真正的已经完成(真正完成该任务是这个任务的run方法执行完毕),只能知道该任务已经出了任务队列,正在执行或者已经完成。

2、java类库中提供的线程池简介:

     java提供的线程池更加强大,相信理解线程池的工作原理,看类库中的线程池就不会感到陌生了。


一,Java的Executor框架


1,Executor接口

[java]  view plain  copy
 print ?
  1. public interface Executor {  
  2.      void execute(Runnable command);  
  3.  }  
Executor接口是Executor框架中最基础的部分,定义了一个用于执行Runnable的execute方法,它没有实现类只有另一个重要的子接口ExecutorService

2,ExecutorService接口

[java]  view plain  copy
 print ?
  1. //继承自Executor接口  
  2.   public interface ExecutorService extends Executor {  
  3.       /** 
  4.        * 关闭方法,调用后执行之前提交的任务,不再接受新的任务 
  5.        */  
  6.       void shutdown();  
  7.       /** 
  8.        * 从语义上可以看出是立即停止的意思,将暂停所有等待处理的任务并返回这些任务的列表 
  9.        */  
  10.      List<Runnable> shutdownNow();  
  11.      /** 
  12.       * 判断执行器是否已经关闭 
  13.       */  
  14.      boolean isShutdown();  
  15.      /** 
  16.       * 关闭后所有任务是否都已完成 
  17.       */  
  18.      boolean isTerminated();  
  19.      /** 
  20.       * 中断 
  21.       */  
  22.     boolean awaitTermination(long timeout, TimeUnit unit)  
  23.          throws InterruptedException;  
  24.      /** 
  25.       * 提交一个Callable任务 
  26.       */  
  27.      <T> Future<T> submit(Callable<T> task);  
  28.      /** 
  29.       * 提交一个Runable任务,result要返回的结果 
  30.       */  
  31.      <T> Future<T> submit(Runnable task, T result);  
  32.      /** 
  33.       * 提交一个任务 
  34.       */  
  35.      Future<?> submit(Runnable task);  
  36.      /** 
  37.       * 执行所有给定的任务,当所有任务完成,返回保持任务状态和结果的Future列表 
  38.       */  
  39.      <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)  
  40.          throws InterruptedException;  
  41.      /** 
  42.       * 执行给定的任务,当所有任务完成或超时期满时(无论哪个首先发生),返回保持任务状态和结果的 Future 列表。 
  43.       */  
  44.      <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,  
  45.                                    long timeout, TimeUnit unit)  
  46.          throws InterruptedException;  
  47.      /** 
  48.       * 执行给定的任务,如果某个任务已成功完成(也就是未抛出异常),则返回其结果。 
  49.       */  
  50.      <T> T invokeAny(Collection<? extends Callable<T>> tasks)  
  51.          throws InterruptedException, ExecutionException;  
  52.      /** 
  53.       * 执行给定的任务,如果在给定的超时期满前某个任务已成功完成(也就是未抛出异常),则返回其结果。 
  54.       */  
  55.      <T> T invokeAny(Collection<? extends Callable<T>> tasks,  
  56.                      long timeout, TimeUnit unit)  
  57.          throws InterruptedException, ExecutionException, TimeoutException;  
  58.  }  
ExecutorService接口继承自Executor接口,定义了终止、提交,执行任务、跟踪任务返回结果等方法

1,execute(Runnable command):履行Ruannable类型的任务,

2,submit(task):可用来提交Callable或Runnable任务,并返回代表此任务的Future对象
3,shutdown():在完成已提交的任务后封闭办事,不再接管新任务,

4,shutdownNow():停止所有正在履行的任务并封闭办事。
5,isTerminated():测试是否所有任务都履行完毕了。,

6,isShutdown():测试是否该ExecutorService已被关闭

3,Executors的静态方法:负责生成各种类型的ExecutorService线程池实例

+newFixedThreadPool(numberOfThreads:int):(固定线程池)ExecutorService 创建一个固定线程数量的线程池,并行执行的线程数量不变,线程当前任务完成后,可以被重用执行另一个任务
+newCachedThreadPool():(可缓存线程池)ExecutorService 创建一个线程池,按需创建新线程,就是有任务时才创建,空闲线程保存60s,当前面创建的线程可用时,则重用它们

+new SingleThreadExecutor();(单线程执行器线程池中只有一个线程,依次执行任务


+new ScheduledThreadPool():线程池按时间计划来执行任务,允许用户设定执行任务的时间

+new SingleThreadScheduledExcutor();线程池中只有一个线程,它按规定时间来执行任务

4,Runnable、Callable、Future接口

Runnable接口:

[java]  view plain  copy
 print ?
  1. // 实现Runnable接口的类将被Thread执行,表示一个基本的任务  
  2.   public interface Runnable {  
  3.       // run方法就是它所有的内容,就是实际执行的任务  
  4.       public abstract void run();  
  5.   }  
Callable接口:与Runnable接口的区别在于它接收泛型,同时它执行任务后带有返回内容

[java]  view plain  copy
 print ?
  1. // Callable同样是任务,与Runnable接口的区别在于它接收泛型,同时它执行任务后带有返回内容  
  2.   public interface Callable<V> {  
  3.       // 相对于run方法的带有返回值的call方法  
  4.       V call() throws Exception;  
  5. }  

Runnable接口和Callable接口的实现类,都可以被ThreadPoolExecutor和ScheduledThreadPoolExecutor执行,他们之间的区别是Runnable不会返回结果,而Callable可以返回结果。

Executors可以把一个Runnable对象转换成Callable对象:

[java]  view plain  copy
 print ?
  1. public static Callable<Object> callable(Runnbale task);  
Executors把一个Runnable和一个待返回的结果包装成一个Callable的API:
[java]  view plain  copy
 print ?
  1. public static<T> Callable<T> callable(Runnbale task,T result);  
当把一个Callable对象(Callable1,Callable2)提交给ThreadPoolExecutor和 Scheduled ThreadPoolExecutor执行时,submit(...)会向我们返回一个FutureTask对象。我们执行FutureTask.get()来等待任务执行完成,当任务完成后,FutureTask.get()将返回任务的结果。


Future接口:

[java]  view plain  copy
 print ?
  1. // Future代表异步任务的执行结果  
  2.   public interface Future<V> {  
  3.     
  4.       /** 
  5.        * 尝试取消一个任务,如果这个任务不能被取消(通常是因为已经执行完了),返回false,否则返回true。 
  6.        */  
  7.       boolean cancel(boolean mayInterruptIfRunning);  
  8.     
  9.       /** 
  10.       * 返回代表的任务是否在完成之前被取消了 
  11.       */  
  12.      boolean isCancelled();  
  13.    
  14.      /** 
  15.       * 如果任务已经完成,返回true 
  16.       */  
  17.     boolean isDone();  
  18.    
  19.      /** 
  20.       * 获取异步任务的执行结果(如果任务没执行完将等待) 
  21.       */  
  22.     V get() throws InterruptedException, ExecutionException;  
  23.    
  24.      /** 
  25.       * 获取异步任务的执行结果(有最常等待时间的限制) 
  26.       * 
  27.       *  timeout表示等待的时间,unit是它时间单位 
  28.       */  
  29.      V get(long timeout, TimeUnit unit)  
  30.          throws InterruptedException, ExecutionException, TimeoutException;  
  31.  }  
Future就是对于具体的Runnable或者Callable任务的执行结果进行取消查询是否完成获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果


在Future接口中声明了5个方法,下面依次解释每个方法的作用:
+cancel方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。
+isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。
+isDone方法表示任务是否已经完成,若任务完成,则返回true;
+get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
+get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。

也就是说Future提供了三种功能:
  1)判断任务是否完成;
  2)能够中断任务;
  3)能够获取任务执行结果。


FutureTask:

通常使用FutureTask来处理我们的任务。FutureTask类同时又实现了Runnable接口,所以可以直接提交给Executor执行

[java]  view plain  copy
 print ?
  1. FutureTask提供了2个构造器:  
  2.   
  3. public FutureTask(Callable<V> callable) {  
  4. }  
  5. public FutureTask(Runnable runnable, V result) {  
  6. }  
  7.   //事实上,FutureTask是Future接口的一个唯一实现类。  

使用FutureTask实现超时执行的代码如下:

[java]  view plain  copy
 print ?
  1. xecutorService executor = Executors.newSingleThreadExecutor();     
  2. FutureTask<String> future =     
  3.        new FutureTask<String>(new Callable<String>() {//使用Callable接口作为构造参数     
  4.          public String call() {     
  5.            //真正的任务在这里执行,这里的返回值类型为String,可以为任意类型     
  6.        }});     
  7. executor.execute(future);     
  8. //在这里可以做别的任何事情     
  9. try {     
  10.     result = future.get(5000, TimeUnit.MILLISECONDS); //取得结果,同时设置超时执行时间为5秒。同样可以用future.get(),不设置执行超时时间取得结果     
  11. catch (InterruptedException e) {     
  12.     futureTask.cancel(true);     
  13. catch (ExecutionException e) {     
  14.     futureTask.cancel(true);     
  15. catch (TimeoutException e) {     
  16.     futureTask.cancel(true);     
  17. finally {     
  18.     executor.shutdown();     
  19. }    


不直接构造Future对象,也可以使用ExecutorService.submit方法来获得Future对象,submit方法即支持以 Callable接口类型,也支持Runnable接口作为参数,具有很大的灵活性。使用示例如下:

[java]  view plain  copy
 print ?
  1. ExecutorService executor = Executors.newSingleThreadExecutor();     
  2. FutureTask<String> future = executor.submit(     
  3.    new Callable<String>() {//使用Callable接口作为构造参数     
  4.        public String call() {     
  5.       //真正的任务在这里执行,这里的返回值类型为String,可以为任意类型     
  6.    }});     
  7. //在这里可以做别的任何事情     
  8. //同上面取得结果的代码   


线程池实现原理详解:

ThreadPoolExecutor是线程池的实现类:

[java]  view plain  copy
 print ?
  1. public ThreadPoolExecutor(int corePoolSize,    
  2.                               int maximumPoolSize,    
  3.                               long keepAliveTime,    
  4.                               TimeUnit unit,    
  5.                               BlockingQueue<Runnable> workQueue,    
  6.                               ThreadFactory threadFactory,    
  7.                               RejectedExecutionHandler handler)     
(1)corePoolSize(线程池的基本大小):当提交一个任务到线程池时,线程会创建一个线程来执行任务,即使其他空闲的基本线程能创建线程也会创建线程,等到到需要执行的任务数大于线程池基本大小corePoolSize时就不再创建

(2)maximumPoolSize(线程池最大大小):线程池允许最大线程数。如果阻塞队列满了,并且已经创建的线程数小于最大线程数,则线程池会再创建新的线程执行。因为线程池执行任务时是线程池基本大小满了,后续任务进入阻塞队列,阻塞队列满了,在创建线程。

(3)keepAliveTime(线程活动保持时间):空闲线程的保持存活时间。
(4)TimeUnit(线程活动保持时间的单位):

        TimeUnit.DAYS; //天
        TimeUnit.HOURS; //小时
        TimeUnit.MINUTES; //分钟
        TimeUnit.SECONDS; //秒
        TimeUnit.MILLISECONDS; //毫秒
        TimeUnit.MICROSECONDS; //微妙
        TimeUnit.NANOSECONDS; //纳秒

(5)workQueue(任务队列):用于保存等待执行的任务的阻塞队列。一个阻塞队列,用来存储等待执行的任务:数组,链表,不存元素的阻塞队列

      5.1)ArrayBlockingQueue;数组结构的有界阻塞队列,先进先出FIFO
      5.2)LinkedBlockingQueue;链表结构的无界阻塞队列。先进先出FIFO排序元素,静态方法Executors.newFixedThreadPool使用这个方法

      5.3)SynchronousQueue;不存储元素的阻塞队列,就是每次插入操作必须等到另一个线程调用移除操作,静态方法Executors.newCachedThreadPool使用这个方法

 (6)threadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字

 (7)handler(饱和策略):表示当拒绝处理任务时的策略。当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。

     ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。

     ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常

     ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)

    ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

 

我们尽量优先使用Executors提供的静态方法来创建线程池,如果Executors提供的方法无法满足要求,再自己通过ThreadPoolExecutor类来创建线程池   

[java]  view plain  copy
 print ?
  1. Executors.newFixedThreadPool(int); //创建固定容量大小的缓冲池         
  2. Executors.newCachedThreadPool(); //创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUE  
  3. Executors.newSingleThreadExecutor(); //创建容量为1的缓冲池  

下面是这三个静态方法的具体实现;

[java]  view plain  copy
 print ?
  1. public static ExecutorService newFixedThreadPool(int nThreads) {  
  2.     return new ThreadPoolExecutor(nThreads, nThreads,  
  3.                                   0L, TimeUnit.MILLISECONDS,  
  4.                                   new LinkedBlockingQueue<Runnable>());  
  5. }  
  6. public static ExecutorService newSingleThreadExecutor() {  
  7.     return new FinalizableDelegatedExecutorService  
  8.         (new ThreadPoolExecutor(11,  
  9.                                 0L, TimeUnit.MILLISECONDS,  
  10.                                 new LinkedBlockingQueue<Runnable>()));  
  11. }  
  12. public static ExecutorService newCachedThreadPool() {  
  13.     return new ThreadPoolExecutor(0, Integer.MAX_VALUE,  
  14.                                   60L, TimeUnit.SECONDS,  
  15.                                   new SynchronousQueue<Runnable>());  
  16. }  

从它们的具体实现来看,它们实际上也是调用了ThreadPoolExecutor,只不过参数都已配置好了。

newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize值是相等的(n,n),它使用的LinkedBlockingQueue


newSingleThreadExecutor将corePoolSize和maximumPoolSize都设置为1(1,1),也使用的LinkedBlockingQueue


newCachedThreadPool将corePoolSize设置为0,将maximumPoolSize设置为Integer.MAX_VALUE,使用的SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程。

实际中,如果Executors提供的三个静态方法能满足要求,就尽量使用它提供的三个方法,因为自己去手动配置ThreadPoolExecutor的参数有点麻烦,要根据实际任务的类型和数量来进行配置。


1)newFixedThreadPool:(固定线程池)

[java]  view plain  copy
 print ?
  1. public static ExecutorService newFixedThreadPool(int nThreads) {  
  2.     return new ThreadPoolExecutor(nThreads, nThreads,  
  3.                                   0L, TimeUnit.MILLISECONDS,  
  4.                                   new LinkedBlockingQueue<Runnable>());  
  5. }  
线程池corePoolSize和maximumPoolSize值是相等的(n,n),把keepAliveTime设置0L,意味着多余的空闲线程会被立即终止。

newFixedThreadPool的execute方法执行过程:

1,如果当前运行线程数少于corePoolSize,则创建新线程来执行任务(优先满足核心池

2,当前运行线程数等于corePoolSize时,将任务加入LinkedBlockingQueue链式阻塞队列(核心池满了在进入队列

3,当线程池的任务完成之后,循环反复从LinkedBlockingQueue队列中获取任务来执行


2)newSingleThreadExecutor:(单线程执行器)

newSingleThreadExecutor是使用单个worker线程的Executors.

[java]  view plain  copy
 print ?
  1. public static ExecutorService newSingleThreadExecutor() {  
  2.     return new FinalizableDelegatedExecutorService  
  3.         (new ThreadPoolExecutor(11,  
  4.                                 0L, TimeUnit.MILLISECONDS,  
  5.                                 new LinkedBlockingQueue<Runnable>()));  
  6. }  
newSingleThreadExecutor的execute方法执行过程如下:

  1,当前运行的线程数少于corePoolSize(即当前线程池中午运行的线程),则创建一个新的线程来执行任务

  2,当线程池中有一个运行的线程时,将任务加入阻塞队列

  3,当线程完成任务时,会无限反复从链式阻塞队列中获取任务来执行


3,)newCachedThreadPool:可缓存线程池

[java]  view plain  copy
 print ?
  1. public static ExecutorService newCachedThreadPool() {  
  2.     return new ThreadPoolExecutor(0, Integer.MAX_VALUE,  
  3.                                   60L, TimeUnit.SECONDS,  
  4.                                   new SynchronousQueue<Runnable>());  
  5. }  
newCachedThreadPool是一个根据需要创建线程的线程池。

newCachedThreadPool的corePoolSize设置0,即核心池是空,maxmumPoolSize设置为Integer.MAX_VALUE,即maxmumPool是无界的。keepAliveTime设置60L,当空闲线程等待新任务最长时间是60s,超过60s就终止

三个线程池的特点:

1、newFixedThreadPool创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数corePoolSize,则将提交的任务存入到池队列中

2、newCachedThreadPool创建一个可缓存的线程池。这种类型的线程池特点是:
1).工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE), 这样可灵活的往线程池中添加线程。
2).如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。

3、newSingleThreadExecutor创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,如果这个线程异常结束,会有另一个取代它,保证顺序执行(我觉得这点是它的特色)。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的 


线程池的处理流程:

线程池执行示意图:


1,首先线程池判断基本线程池是否已满(< corePoolSize ?)?没满,创建一个工作线程来执行任务。满了,则进入下个流程。

2,其次线程池判断工作队列是否已满?没满,则将新提交的任务存储在工作队列里。满了,则进入下个流程。

3,最后线程池判断整个线程池是否已满(< maximumPoolSize ?)?没满,则创建一个新的工作线程来执行任务,满了,则交给饱和策略来处理这个任务。

总结:线程池优先要创建出基本线程池大小(corePoolSize)的线程数量,没有达到这个数量时,每次提交新任务都会直接创建一个新线程,当达到了基本线程数量后,又有新任务到达,优先放入等待队列,如果队列满了,才去创建新的线程(不能超过线程池的最大数maxmumPoolSize)


向线程池提交任务的两种方式:

1)通过execute()方法

[java]  view plain  copy
 print ?
  1. ExecutorService threadpool= Executors.newFixedThreadPool(10);    
  2. threadpool.execute(new Runnable(){...});  
这种方式提交 没有返回值,也就不能判断任务是否被线程池执行成功

2)通过submit()方法

[java]  view plain  copy
 print ?
  1. Future<?> future = threadpool.submit(new Runnable(){...});    
  2.     try {    
  3.             Object res = future.get();//获取任务执行结果    
  4.         } catch (InterruptedException e) {    
  5.             // 处理中断异常    
  6.             e.printStackTrace();    
  7.         } catch (ExecutionException e) {    
  8.             // 处理无法执行任务异常    
  9.             e.printStackTrace();    
  10.         }finally{    
  11.             // 关闭线程池    
  12.             executor.shutdown();    
  13.         }    
使用submit 方法来提交任务,它会返回一个Future对象,通过future的get方法来获取返回值,get方法会阻塞住直到任务完成,而使用get(long timeout, TimeUnit unit)方法则会阻塞一段时间后立即返回,这时有可能任务没有执行完。


线程池的关闭:

• shutdown():不会立即终止线程池,而是再也不会接受新的任务要等所有任务缓存队列中的任务都执行完后才终止
• shutdownNow():立即终止线程池,再也不会接受新的任务,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务

线程池本身的状态

[java]  view plain  copy
 print ?
  1. volatile int runState;     
  2. static final int RUNNING = 0;   //运行状态  
  3. static final int SHUTDOWN = 1;   //关闭状态  
  4. static final int STOP = 2;       //停止  
  5. static final int TERMINATED = 3//终止,终结  

1,当创建线程池后,初始时,线程池处于RUNNING状态
2,如果调用了shutdown()方法,则线程池处于SHUTDOWN状态,此时线程池不能够接受新的任务,它会等待所有任务执行完毕,最后终止;
3,如果调用了shutdownNow()方法,则线程池处于STOP状态,此时线程池不能接受新的任务,并且会去尝试终止正在执行的任务,返回没有执行的任务列表;
4,当线程池处于SHUTDOWN或STOP状态,并且所有工作线程已经销毁,任务缓存队列已经清空或执行结束后,线程池被设置为TERMINATED状态


使用线程池的好处:

1.减少在创建和销毁线程上所花的时间以及系统资源的开销 
2.如不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存

以下是Java自带的几种线程池:

1、newFixedThreadPool 创建一个指定工作线程数量的线程池。

每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。

2、newCachedThreadPool 创建一个可缓存的线程池。

这种类型的线程池特点是:

1).工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE), 这样可灵活的往线程池中添加线程。

2).如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。

3、newSingleThreadExecutor 创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,如果这个线程异常结束,会有另一个取代它,保证顺序执行(我觉得这点是它的特色)。

单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的 。

4、newScheduleThreadPool 创建一个定长的线程池,而且支持定时的以及周期性的任务执行,类似于Timer。

总结:

一.FixedThreadPool是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。但在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。

二.CachedThreadPool的特点就是在线程池空闲时,即线程池中没有可运行任务时,它会释放工作线程,从而释放工作线程所占用的资源。但是,但当出现新任务时,又要创建一新的工作线程,又要一定的系统开销。并且,在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪。

在ThreadPoolExecutor类中提供了四个构造方法:

[java]  view plain  copy
  1.  public class ThreadPoolExecutor extends AbstractExecutorService {  
  2.     ......  
  3.     public ThreadPoolExecutor(int corePoolSize,  
  4.                               int maximumPoolSize,  
  5.                               long keepAliveTime,  
  6.                               TimeUnit unit,  
  7.                               BlockingQueue<Runnable> workQueue)  
  8.   
  9.     public ThreadPoolExecutor(int corePoolSize,  
  10.                               int maximumPoolSize,  
  11.                               long keepAliveTime,  
  12.                               TimeUnit unit,  
  13.                               BlockingQueue<Runnable> workQueue,  
  14.                               ThreadFactory threadFactory)  
  15.   
  16.     public ThreadPoolExecutor(int corePoolSize,  
  17.                               int maximumPoolSize,  
  18.                               long keepAliveTime,  
  19.                               TimeUnit unit,  
  20.                               BlockingQueue<Runnable> workQueue,  
  21.                               RejectedExecutionHandler handler)  
  22.   
  23.     public ThreadPoolExecutor(int corePoolSize,//核心线程数  
  24.                               int maximumPoolSize,//最大线程数  
  25.                               long keepAliveTime,//闲置线程存活时间  
  26.                               TimeUnit unit,//时间单位  
  27.                               BlockingQueue<Runnable> workQueue,//线程队列  
  28.                               ThreadFactory threadFactory,//线程工厂  
  29.                               RejectedExecutionHandler handler//队列已满,而且当前线程数已经超过最大线程数时的异常处理策略)  
  30. }  

corePoolSize:核心线程数,核心线程会一直存活,即使没有任务需要处理。当线程数小于核心线程数时,即使现有的线程空闲,线程池也会优先创建新   线程来处理任务,而不是直接交给现有的线程处理。核心线程在allowCoreThreadTimeout被设置为true时会超时退出,默认情况下不会退   出。

maxPoolSize:当线程数大于或等于核心线程,且任务队列已满时,线程池会创建新的线程,直到线程数量达到maxPoolSize。如果线程数已等于     maxPoolSize,且任务队列已满,则已超出线程池的处理能力,线程池会拒绝处理任务而抛出异常。

keepAliveTime: 当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize。如果allowCoreThreadTimeout设置为true,则所             有线程均会退出直到线程数量为0。

unit:参数keepAliveTime的时间单位,有7种取值,从天到纳秒。

workQueue:一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列分三种,分别是:ArrayBlockingQueue;LinkedBlockingQueue;SynchronousQueue;

threadFactory:线程工厂,主要用来创建线程;

handler:表示当拒绝处理任务时的策略,有以下四种取值:

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务 

接下来我们看看线程池的处理流程图:


再接下来,我们看一张图来了解 ThreadPoolExecutor、AbstractExecutorService、ExecutorService和Executor几个之间的关系:


有兴趣的可以查查这些类中具体的方法,我这里就不一一列出了。

在java doc中,并不提倡我们直接使用ThreadPoolExecutor,而是使用Executors类中提供的几个静态方法来创建线程池:

Executors.newCachedThreadPool();         //创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUE
Executors.newSingleThreadExecutor();    //创建容量为1的缓冲池
Executors.newFixedThreadPool( int );     //创建固定容量大小的缓冲池
三个静态方法的具体实现:

[java]  view plain  copy
  1. public static ExecutorService newFixedThreadPool(int nThreads) {  
  2.     return new ThreadPoolExecutor(nThreads, nThreads,  
  3.                                   0L, TimeUnit.MILLISECONDS,  
  4.                                   new LinkedBlockingQueue<Runnable>());  
  5. }  
  6. public static ExecutorService newSingleThreadExecutor() {  
  7.     return new FinalizableDelegatedExecutorService  
  8.         (new ThreadPoolExecutor(11,  
  9.                                 0L, TimeUnit.MILLISECONDS,  
  10.                                 new LinkedBlockingQueue<Runnable>()));  
  11. }  
  12. public static ExecutorService newCachedThreadPool() {  
  13.     return new ThreadPoolExecutor(0, Integer.MAX_VALUE,  
  14.                                   60L, TimeUnit.SECONDS,  
  15.                                   new SynchronousQueue<Runnable>());  
  16. }  
从源码可以看出:这三个方法其实都是调用了 ThreadPoolExecutor,只不过参数都已配置好了。

newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize值是相等的,它使用的LinkedBlockingQueue;

newSingleThreadExecutor将corePoolSize和maximumPoolSize都设置为1,也使用的LinkedBlockingQueue;

newCachedThreadPool将corePoolSize设置为0,将maximumPoolSize设置为Integer.MAX_VALUE,使用的SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程。

在实际开发中,如果Executors提供的三个静态方法能满足要求,就尽量使用它提供的三个方法。

这是线程池源码中的原话:

  1. 如果当前正在运行的线程数小于核心线程数,即使线程 
  2.  池中有空闲的线程,仍创建一个新线程处理任务执行请求,即创建一个新的任务线程执行任务。

那么为什么不用空闲线程而要创建线程呢。答:因为线程池复用核心线程的方法是从任务队列中取任务,而在源码中,只有任务数超过核心线程数才能将任务放入任务队列。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值