并发编程-线程池(一)线程池

一、为什么要用线程池

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

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

  3、提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、调优和监控

 

二、ThreadPoolExecutor线程池类参数详解

       

 

 

 当线程池任务处理不过来的时候(什么时候认为处理不过来后面描述),可以通过handler指定的策略进行处理,ThreadPoolExecutor提供了四种策略:

  ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常;也是默认的处理方式。
  ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。
  ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
  ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
  可以通过实现RejectedExecutionHandler接口自定义处理方式。

 

三. 线程池任务执行

线程池状态  

线程池的5种状态:Running、ShutDown、Stop、Tidying、Terminated。

 

 

线程池状态转化【2】

 

 1、RUNNING

(1) 状态说明:线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。 

(2) 状态切换:线程池的初始化状态是RUNNING。换句话说,线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0!

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

2、 SHUTDOWN

(1) 状态说明:线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。 

(2) 状态切换:调用线程池的shutdown()接口时,线程池由RUNNING -> SHUTDOWN。

3、STOP

(1) 状态说明:线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。 

(2) 状态切换:调用线程池的shutdownNow()接口时,线程池由(RUNNING or SHUTDOWN ) -> STOP。

4、TIDYING

(1) 状态说明:当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进

行相应的处理;可以通过重载terminated()函数来实现。 

(2) 状态切换:当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。 

当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。

5、 TERMINATED

(1) 状态说明:线程池彻底终止,就变成TERMINATED状态。 

(2) 状态切换:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。

 

3.1. 添加执行任务
  submit() 该方法返回一个Future对象,可执行带返回值的线程;或者执行想随时可以取消的线程。Future对象的get()方法获取返回值。Future对象的cancel(true/false)取消任务,未开始或已完成返回false,参数表示是否中断执行中的线程
  execute() 没有返回值。


3.2. 线程池任务提交过程
  一个线程提交到线程池的处理流程如下图

         

 

 

1、如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
2、如果此时线程池中的数量等于corePoolSize,但是缓冲队列workQueue未满,那么任务被放入缓冲队列。
3、如果此时线程池中的数量大于等于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
4、如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。
5、当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。
总结即:处理任务判断的优先级为 核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。

(注意:

1、当workQueue使用的是无界限队列时,maximumPoolSize参数就变的无意义了,比如new LinkedBlockingQueue(),或者new ArrayBlockingQueue(Integer.MAX_VALUE);
2、使用SynchronousQueue队列时由于该队列没有容量的特性,所以不会对任务进行排队,如果线程池中没有空闲线程,会立即创建一个新线程来接收这个任务。maximumPoolSize要设置大一点。
3、核心线程和最大线程数量相等时keepAliveTime无作用.

 

3.3. 线程池关闭
1、shutdown() 不接收新任务,会处理已添加任务
2、shutdownNow() 不接受新任务,不处理已添加任务,中断正在处理的任务

线程池回收:

 

4. 常用队列介绍
ArrayBlockingQueue: 这是一个由数组实现的容量固定的有界阻塞队列.
SynchronousQueue: 没有容量,不能缓存数据;每个put必须等待一个take; offer()的时候如果没有另一个线程在poll()或者take()的话返回false。
LinkedBlockingQueue: 这是一个由单链表实现的默认无界的阻塞队列。LinkedBlockingQueue提供了一个可选有界的构造函数,而在未指明容量时,容量默认为Integer.MAX_VALUE。

 

5. Executors线程工厂类
1、Executors.newCachedThreadPool();
说明:

   创建的线程池核心线程0 , 最大线程是Integer.MaxValue。 线程空闲存活时间1分钟。 默认异常拒绝策略,使用SynchronousQueue队

特点:

  每次添加任务如果没有空闲线程就会新建一个线程去执行。
  SynchronousQueue是阻塞队列,加入任务的线程会阻塞住,直到其它线程从中取走任务才会结束阻塞
  线程创建上限近乎无限

适用场景:

  所以它适用于任务加入比较稳当且加入间隔短的场景

实现:

  new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.SECONDS,new SynchronousQueue());

2、Executors.newFixedThreadPool(int);
说明: 

  核心线程和最大线程数是你传入的参数。 其他参数和 Executors.newSingleThreadExecutor一样

实现:

  new ThreadPoolExecutor(nThreads, nThreads,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue());

3、Executors.newSingleThreadExecutor();
说明:

  创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照顺序执行。

特点:

  只有一个线程

  近乎可以接收无限任务的队列, 可以堆积大量任务

  适用于任务持续加入但是任务数并不多的场景

实现:

  new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue())

4、Executors.newScheduledThreadPool(int);
说明:

  创建一个定长线程池,支持定时及周期性任务执行。

特点:

  核心线程是传入的参数,最大线程是int上线, 默认存活时间是10毫秒, 任务队列使用自己实现的DelayedWorkQueue, 拒绝策略异常策略

  加入任务的时候,会把任务和定时时间构建一个RunnableScheduledFuture对象,再把这个对象放入DelayedWorkQueue队列中,

  DelayedWorkQueue是一个有序队列, 他会根据内部的RunnableScheduledFuture的运行时间排序内部对象。

  任务加入后就会启动一个线程。 这个线程会从DelayedWorkQueue中获取一个任务。

  DelayedWorkQueue内部是按照时间从前完后获取任务的。如果任务的中的时间还没有到。 获取的就是null。 获取任务结束,线程会休眠10毫秒。所以这个定时任务的执行最小间隔是10毫秒的。

内部实现

  new ScheduledThreadPoolExecutor(corePoolSize)

 

五、线程池API --- 接口定义和实现类

 

 

---------------------------------------------------------------------------------------------------------------------------------------

[1] https://www.cnblogs.com/spec-dog/p/11149741.html

[2] https://blog.csdn.net/shahuhubao/article/details/80311992 

posted @ 2020-09-14 22:56  李荣先辈Java  阅读(238)  评论(0编辑  收藏  举报