Java并发---- Executor并发框架--线程池,ThreadToolExecutor初步理解

一、线程池

    对于数据库连接,我们经常听到数据库连接池这个概念。因为建立数据库连接时非常耗时的一个操作,其中涉及到网络IO的一些操作。因此就想出把连接通过一个连接池来管理。需要连接的话,就从连接池里取一个。当使用完了,就“关闭”连接,这不是正在意义上的关闭,只是把连接放回到我们的池里,供其他人在使用。所以对于线程,也有了线程池这个概念,其中的原理和数据库连接池是差不多的,因此Java jdk中也提供了线程池的功能。
    线程池的作用:线程池就是限制系统中使用线程的数量以及更好的使用线程
    根据系统的运行情况,可以自动或手动设置线程数量,达到运行的最佳效果:配置少了,将影响系统的执行效率,配置多了,又会浪费系统的资源。用线程池配置数量,其他线程排队等候。当一个任务执行完毕后,就从队列中取一个新任务运行,如果没有新任务,那么这个线程将等待。如果来了一个新任务,但是没有空闲线程的话,那么把任务加入到等待队列中。
为什么要用线程池:
    1.减少线程创建和销毁的次数,使线程可以多次复用
    2.可以根据系统情况,调整线程的数量。防止创建过多的线程,消耗过多的内存(每个线程1M左右)
Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。Executors类,提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService接口。

比较重要的几个类:

ExecutorService

真正的线程池接口。

ScheduledExecutorService

能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。

ThreadPoolExecutor

ExecutorService的默认实现。

ScheduledThreadPoolExecutor

继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。

    要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在Executors类里面提供了一些静态工厂,生成一些常用的线程池。

java通过Executors提供四种线程池,分别为:
    1、Executors.newCachedThreadPool(),创建一个可缓存线程池,缓冲池容量大小为Integer.MAX_VALUE,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
    2、Executors.newFixedThreadPool(int) 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
    3、newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
    4、Executors.newSingleThreadExecutor() 创建一个单线程化(容量为1)的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

1-1.newSingleThreadExecutor

Executors类里的两个静态实现方法

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <pre name="code" class="java">    public static ExecutorService newSingleThreadExecutor() {  
  2.         return new FinalizableDelegatedExecutorService  
  3.             (new ThreadPoolExecutor(11,  
  4.                                     0L, TimeUnit.MILLISECONDS,  
  5.                                     new LinkedBlockingQueue<Runnable>()));  
  6.     }  
  7.   
  8.     public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {  
  9.         return new FinalizableDelegatedExecutorService  
  10.             (new ThreadPoolExecutor(11,  
  11.                                     0L, TimeUnit.MILLISECONDS,  
  12.                                     new LinkedBlockingQueue<Runnable>(),  
  13.                                     threadFactory));  
  14.     }  
 
    创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。测试: 

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * 4、Executors.newSingleThreadExecutor() 创建一个单线程化(容量为1)的线程池,它只会用唯一的工作线程来执行任务, 
  3.  * 保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。 
  4.  */  
  5. public static void main(String[] args) {  
  6.     ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();  
  7.     for (int i = 0; i < 10; i++) {  
  8.         final int index = i;  
  9.         singleThreadExecutor.execute(new Runnable() {  
  10.             public void run() {  
  11.                 try {  
  12.                     System.out.println("ThreadName:" + Thread.currentThread().getName()+","+index);  
  13.                     Thread.sleep(1000);  
  14.                 } catch (InterruptedException e) {  
  15.                     e.printStackTrace();  
  16.                 }  
  17.             }  
  18.         });  
  19.     }  
  20. }  
ThreadName:pool-1-thread-1,0ThreadName:pool-1-thread-1,1ThreadName:pool-1-thread-1,2ThreadName:pool-1-thread-1,3ThreadName:pool-1-thread-1,4......
线程名都一样,说明是同一个线程

1-2.newFixedThreadPool

     Executors类里的构造方法,可以看到,corePoolSize和maximumPoolSize的大小是一样的(实际上,后面会介绍,如果使用无界queue的话 maximumPoolSize参数是没有意义的),keepAliveTime和unit的设值表明什么?-就是该实现不想keep alive!最后的BlockingQueue选择了LinkedBlockingQueue,该queue有一个特点,他是无界的。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <pre name="code" class="java"><pre name="code" class="java">    public static ExecutorService newFixedThreadPool(int nThreads) {  
  2.         return new ThreadPoolExecutor(nThreads, nThreads,  
  3.                                       0L, TimeUnit.MILLISECONDS,  
  4.                                       new LinkedBlockingQueue<Runnable>());  
  5.     }  
  6.   
  7.     public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {  
  8.         return new ThreadPoolExecutor(nThreads, nThreads,  
  9.                                       0L, TimeUnit.MILLISECONDS,  
  10.                                       new LinkedBlockingQueue<Runnable>(),  
  11.                                       threadFactory);  
  12.     }  
 
 
    创建 
固定大小 
的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。 
线程池的大小一旦达到最大值就会保持不变 
,在提交新任务,任务将会进入等待队列中等待。如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。 

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public static void main(String[] args) {  
  2.     /** 
  3.      * 2、Executors.newFixedThreadPool(int) 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。 
  4.      * 
  5.      * 因为线程池大小为3,每个任务输出index后sleep 2秒,所以每两秒打印3个数字。 
  6.      * 定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors() 
  7.      */  
  8.     ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);  
  9.     for (int i = 0; i < 10; i++) {  
  10.         final int index = i;  
  11.         fixedThreadPool.execute(new Runnable() {  
  12.             public void run() {  
  13.                 try {  
  14.                     System.out.println("ThreadName:" + Thread.currentThread().getName()+","+index);  
  15.                     Thread.sleep(1000);  
  16.                 } catch (InterruptedException e) {  
  17.                     e.printStackTrace();  
  18.                 }  
  19.             }  
  20.         });  
  21.     }  
  22. }  

ThreadName:pool-1-thread-1,0ThreadName:pool-1-thread-2,1ThreadName:pool-1-thread-3,2ThreadName:pool-1-thread-3,3ThreadName:pool-1-thread-2,4ThreadName:pool-1-thread-1,5。。。。。。

1-3.newCachedThreadPool

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. //这个实现就有意思了。首先是无界的线程池,所以我们可以发现maximumPoolSize为big big。其次BlockingQueue的选择上使用SynchronousQueue。  
  2. 可能对于该BlockingQueue有些陌生,简单说:该 QUEUE中,每个插入操作必须等待另一个线程的对应移除操作。  
  3. public static ExecutorService newCachedThreadPool() {  
  4.       return new ThreadPoolExecutor(0, Integer.MAX_VALUE,  
  5.                                     60L, TimeUnit.SECONDS,  
  6.                                     new SynchronousQueue<Runnable>());  
  7.   }  
  8.   
  9.   public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {  
  10.       return new ThreadPoolExecutor(0, Integer.MAX_VALUE,  
  11.                                     60L, TimeUnit.SECONDS,  
  12.                                     new SynchronousQueue<Runnable>(),  
  13.                                     threadFactory);  
  14.   }  

    创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒处于等待任务到来)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池的最大值是Integer的最大值(2^31-1)。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public static void main(String[] args) {  
  2.     /** 
  3.      * 1、Executors.newCachedThreadPool(),创建一个可缓存线程池,缓冲池容量大小为Integer.MAX_VALUE, 
  4.      * 如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 
  5.      * 
  6.      * 线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。 
  7.      */  
  8.     ExecutorService cachedThreadPool = Executors.newCachedThreadPool();  
  9.     for (int i = 0; i < 10; i++) {  
  10.         final int index = i;  
  11.         try {  
  12.             Thread.sleep( 1000);  
  13.         } catch (InterruptedException e) {  
  14.             e.printStackTrace();  
  15.         }  
  16.         //任务提交  
  17.         cachedThreadPool.execute(new Runnable() {  
  18.             public void run() {  
  19.                 //线程名一样  
  20.                 System.out.println("ThreadName:" + Thread.currentThread().getName()+","+index);  
  21.             }  
  22.         });  
  23.     }  
  24.     /** 
  25.      * ThreadPoolExecutor提供了两个方法,用于线程池的关闭,分别是shutdown()和shutdownNow(),其中: 
  26.      * shutdown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务 
  27.      * shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务 
  28.      */  
  29.     cachedThreadPool.shutdownNow();  
  30. }  
ThreadName:pool-1-thread-1,0ThreadName:pool-1-thread-1,1ThreadName:pool-1-thread-1,2ThreadName:pool-1-thread-1,3ThreadName:pool-1-thread-1,4......
    线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。

1-4.newScheduledThreadPool

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <pre name="code" class="java">    public static ExecutorService newSingleThreadExecutor() {  
  2.         return new FinalizableDelegatedExecutorService  
  3.             (new ThreadPoolExecutor(11,  
  4.                                     0L, TimeUnit.MILLISECONDS,  
  5.                                     new LinkedBlockingQueue<Runnable>()));  
  6.     }  
  7.   
  8.     public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {  
  9.         return new FinalizableDelegatedExecutorService  
  10.             (new ThreadPoolExecutor(11,  
  11.                                     0L, TimeUnit.MILLISECONDS,  
  12.                                     new LinkedBlockingQueue<Runnable>(),  
  13.                                     threadFactory));  
  14.     }  
 
    创建一个大小无限的线程池。此线程池支持 
定时 
以及 
周期性 
执行任务的需求。 

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public static void main(String[] args) {  
  2.     /** 
  3.      * 3、newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。 
  4.      */  
  5.     //延迟执行示例代码如下:  
  6.       ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);  
  7.     scheduledThreadPool.schedule(new Runnable() {  
  8.         public void run() {  
  9.             System.out.println("delay 3 seconds");  
  10.         }  
  11.     }, 3, TimeUnit.SECONDS);  //延迟3秒后执行run方法。  
  12.   
  13.     // 定期执行示例代码如下:  
  14.     ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);  
  15.     scheduledThreadPool.scheduleAtFixedRate(new Runnable() {  
  16.         public void run() {  
  17.             System.out.println("delay 1 seconds, and excute every 3 seconds");  
  18.         }  
  19.     }, 13, TimeUnit.SECONDS); //延迟1秒后每3秒执行一次。  
  20. }  

二、ThreadToolExecutor详解

      java.uitl.concurrent.ThreadPoolExecutor类是线程池中最核心的一个类,因此如果要透彻地了解Java中的线程池,必须先了解这个类。下面我们来看一下ThreadPoolExecutor类的具体实现源码。
  在ThreadPoolExecutor类中提供了四个构造方法:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public class ThreadPoolExecutor extends AbstractExecutorService {  
  2.     ....  
  3.     public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,  
  4.                               BlockingQueue<Runnable> workQueue) {  
  5.   
  6.         this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,  
  7.                 Executors.defaultThreadFactory(), defaultHandler);  
  8.     }  
  9.   
  10.     public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,  
  11.                               BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {  
  12.   
  13.         this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,  
  14.                 threadFactory, defaultHandler);  
  15.     }  
  16.   
  17.     public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,  
  18.                               BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {  
  19.   
  20.         this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,  
  21.                 Executors.defaultThreadFactory(), handler);  
  22.     }  
  23.   
  24.     public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,  
  25.                               BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {  
  26.         if (corePoolSize < 0 || maximumPoolSize <= 0 ||  
  27.                 maximumPoolSize < corePoolSize || keepAliveTime < 0)  
  28.             throw new IllegalArgumentException();  
  29.   
  30.         if (workQueue == null || threadFactory == null || handler == null)  
  31.             throw new NullPointerException();  
  32.   
  33.         this.corePoolSize = corePoolSize;  
  34.         this.maximumPoolSize = maximumPoolSize;  
  35.         this.workQueue = workQueue;  
  36.         this.keepAliveTime = unit.toNanos(keepAliveTime);  
  37.         this.threadFactory = threadFactory;  
  38.         this.handler = handler;  
  39.     }  
  40.     ....  
  41.  }  
    从上面的代码可以得知,ThreadPoolExecutor继承了AbstractExecutorService类,并提供了四个构造器,事实上,通过观察每个构造器的源码具体实现,发现前面三个构造器都是调用的第四个构造器进行的初始化工作

    corePoolSize:核心池的大小,这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后,默认情况下,线程池中并没有 任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者 prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建 corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线 程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;在没有设置allowCoreThreadTimeOut为true情况下,核心线程不会销毁;
    maximumPoolSize:线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程;该值实际的可设置最大值不是Integer.MAX_VALUE,而是常量CAPACITY
    keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize 时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize。即当线程池中的线程数大于corePoolSize 时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了 allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize 时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
    unit:参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. TimeUnit.DAYS;               //天  
  2. TimeUnit.HOURS;             //小时  
  3. TimeUnit.MINUTES;           //分钟  
  4. TimeUnit.SECONDS;           //秒  
  5. TimeUnit.MILLISECONDS;      //毫秒  
  6. TimeUnit.MICROSECONDS;      //微妙  
  7. TimeUnit.NANOSECONDS;       //纳秒  
TimeUnit.SECONDS.sleep(1);相当于 Thread.sleep(1000);
     workQueue: 一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有以下几种选择:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. ArrayBlockingQueue; //有界队列  
  2. LinkedBlockingQueue; //无界队列  
  3. SynchronousQueue; //特殊的一个队列,只有存在等待取出的线程时才能加入队列,可以说容量为0,是无界队列  
     ArrayBlockingQueue和PriorityBlockingQueue使用较少,一般使用LinkedBlockingQueue和Synchronous。线程池的排队策略与BlockingQueue有关。
     threadFactory :线程工厂,主要用来创建线程;
     handler :表示当拒绝处理任务时的策略,有以下四种取值:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。   
  2. ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。   
  3. ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)  
  4. ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务   
ThreadPoolExecutor是Executors类的底层实现。
在JDK帮助文档中,有如此一段话:
    “ 强烈建议程序员使用较为方便的 Executors 工厂方法 Executors.newCachedThreadPool()(无界线程池,可以进行自动线程回收)、Executors.newFixedThreadPool(int)(固定大小线程池)Executors.newSingleThreadExecutor()(单个后台线程)
    它们均为大多数使用场景预定义了设置。”

通过上面的4个构造函数创建完线程池后,就可以通过submit或execute方法提交任务;
工作完成后当然最后要关闭线程池,可以调用下面两个方法:
  - shutdown():对已经在执行的线程进行关闭,不再接收新的任务;
  - shutdownNow():尝试停止正在执行的线程,会清除任务队列中的任务;
以上两个方法都不会等到所有任务完成后关闭,需要通过awaitTermination(long, TimeUnit)方法实现;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值