Java中的线程池

1.线程池的好处:

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

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

3.提高线程的可管理性线程池可以对线程进行统一分配,调优和监控

2.线程池的实现原理

(1)线程池判断核心线程池当中的线程是否都在执行任务.是则执行当前线程,否则下一步

(2)线程池判断工作队列是否已满,未满则添加该任务到工作队列,已满则进入下一步

(3)判断所有线程是否都处于工作状态,如果没有则创建一个新的工作线程来执行任务

3.ThreadPoolExecutor执行分成了4种情况

(1)如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(创建新线程需要获取全局锁)

(2)如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQueue

(3)如果队列已经满了,则创建新的线程来处理任务(创建新线程将获取全局锁)

(4)如果创建新线程将使得当前运行的线程超出maximumPoolSize,任务将会被拒绝,并且调用RejectedExecutionHandler.rejectedExecution()方法

备注:在线程池的使用当中要尽可能的避免获取全局锁,大多数情况下,都会执行步骤2,该步骤是不需要获取全局锁的,全局锁可能会导致一个严重的可伸缩瓶颈,会导致整体的性能下降

4.工作线程:线程池创建线程时,会将线程封装成工作线程Worker,Worker在执行完任务后,还会循环获取工作队列当中的任务来进行执行

5.线程任务的执行

(1)在execute()方法中创建一个线程时,会让这个线程执行当前任务

(2)这个线程执行完任务之后,会反复的从BlockingQueue获取任务来执行

6.线程池的使用

(1)线程池的创建:创建参数

①CorePoolSize:线程池的基本大小

②RunnableTaskQueue:任务队列,用于保存等待执行的任务的阻塞队列

1)ArrayBlockingQueue:基于数组结构的有界阻塞队列,按照FIFO原则排序

2)LinkedBlockingQueue:基于链表结构的阻塞队列,按照FIFO排序

3)SynchronousQueue:一个不储存元素的阻塞队列

4)PriorityBlockingQueue:具有优先级的无线阻塞队列

③MaximumPooolSize:线程池的最大数量:线程池允许创建的最大线程数,这个数量应该等于线程池的基本大小+阻塞队列当中的线程池,但是如果使用的是无界的任务队列,那么这个参数就没有什么效果了

④ThreadFactory:创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字

⑤RejectedExectionHandler:饱和策略,当队列和线程池都已经满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务,有四种处理方式

1)AbortPolicy:直接抛出异常

2)CallerRunsPolicy:只用调用者所在线程来运行任务

3)DIscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务

4)DIscardPolicy:不处理,丢弃掉

⑥饱和策略的其他实现方式:

1)KeepAliveTime:设置线程活动的保持时间,设置它可能会过期的时间,用于保证所有线程的活性

2)TimeUnit:线程活动保持时间的单位

(2)向线程池提交任务

①Execute():不需要返回值的任务

②Submit():提交需要返回值的任务.线程池会返回一个future累心对象,这个future可以对象可以用于判断任务是否执行成功,并且可以通过future的get()方法来获取返回值

(3)关闭线程池

①Shutdown或shutdownNow方法来关闭线程池.原理是便利线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程

②只要调用了这两个关闭方法中的任意一个,isShutdown方法就会返回true.当所有任务关闭之后,才表示线程池已经关闭.此时isTerminaed方法会返回true.

(4)合理的配置线程池

①根据任务特性来分析线程池的配置

1)任务的性质:CPU密集型/IO密集型任务/混合型任务

a.CPU密集型的任务配置应尽可能的小,假设CPU有N个,则配置N+1个

b.IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,假设CPU有N个,则配置2*N,混合型的任务

2)任务优先级:高/中/低

3)任务的执行时间:长/中/段

4)任务的依赖性:是否依赖其他系统资源.例如数据库的连接

备注:如果一直有优先级高的任务提交到队列当中,那么优先级低的任务将永远不会被执行

5)执行时间不同的任务可以交给不同规模的线程池来处理,或者可以使用优先级队列,让执行时间短的任务先执行

6)依赖数据库连接池的任务,因为线程提交SQL后需要等待数据库返回结果,等待时间越长,则CPU空闲时间就越长,那么线程数应该设置的越大,这样才能更好的利用CPU

7)建议使用有界队列,有界队列能增加系统的稳定性和预警能力,可以根据需要设置的大一点

(5)线程池的监控

如果在系统中大量使用线程池,则有必要对线程池进行监控,方便在出现问题的时候更方便找到问题所在,监控线程池时可以使用以下属性

①TaskCount:线程池需要执行的任务数量

②CompletedTaskCount:线程池在运行过程中已完成的数量,限制小于等于taskCount

③LargestPoolSize:线程池当中曾经创建过的最大的线程数量.通过这个数据可以知道线程池是否曾经满过

④GetPoolSize:线程池的线程数量.如果线程池不销毁的话,线程池的线程不会自动销毁,所以这个大小只曾不减少

⑤GetActiveCount:获取活动的线程数

备注:通过拓展线程池来自定义线程池,重写线程池的beforeExecute,afterExecute和terminated方法,也可以在任务执行之前,执行后和线程池关闭前执行一些代码来进行监控.这些方法在线程池当中都是空方法

第10章Executor框架

背景介绍:

在Java中,使用线程来异步执行任务.Java线程的创建与销毁需要一定的开销,如果我们为每一个任务都创建一个新的线程来执行,那么这些线程的创建与销毁将消耗大量的计算资源.同时,为每一个任务创建一个新的线程来执行,这种策略可能会处于高负荷状态的应用最终崩溃

Java的线程即是工作单元,也是执行机制.从JDK5开始,就已经把工作单元和执行机制分离开来.工作单元包括Runnable和Callable,而执行机制有Executor框架提供

1.Exevutor接口的实现类

(1)ThreadPoolExecutor是线程池的核心实现类,用来被提交的任务

(2)ScheduledThreadPoolExecutor 可以在给定的延迟后运行命令,或者定期执行命令

简而言之:执行线程需要去实现Runnable或者是Callable接口

2.3种ThreadPoolExector

(1)FixedThreadPool:需要限制当前线程数量的应用场景,它使用于负载比较重的服务器

(2)SingleThreadExector:适用于需要保证顺序的执行各个人物,并且在任意时间点,不会有多个线程是活动的应用场景

(3)CachedThreadPool : 适用于执行很多的短期异步任务的小程序,或者是负载比较轻的服务器

3.2种ScheduledThreadPoolExecutoor

(1)ScheduledThreadPoolExecutor :包含若干个线程的ScheduledThreadPoolExecutor

(2)SinggleThreadScheduledExecutor : 只包含一个县城的ScheduledThreadPoolExecutor

4.Future接口

线程执行完成之后,会返回一个Future接口的实现类FutureTask用于返回执行的结果

备注:不管是Runnable接口还是Callable接口的实现类,都可以被ThreadPoolExecutor或者是Scheduled-ThreadPoolExector执行,它们之间的区别就是Runnbale接口不会返回结果,而Callable可以返回结果

5.线程池的3种类型

(1)FixedThreadPool:可重用固定线程数的线程池,使用无界队列LinkedBlockingQueue作为线程池的工作队列

(2)使用无界队列的影响:

①当线程池中的线程数达到corePoolSize之后,新任务将在无界队列当中等待,因此线程池当中的线程数不会超过corePoolSize

②使用无界队列时,maximumPoolSize将是一个无效参数

③KeepAliveTime也将是一个无效参数

④运行中的FixedThreadPool,即是未执行方法shutdown或者shutdownNow不会拒绝任务

6.ScheduledThreadPoolExecutor详解

ScheduledThreadPoolExecutor继承自ThreadPoolExecutor.主要用于给定延迟之后运行任务,或者定期执行任务,功能跟Timer类似,单ScheduledThreadPoolExecutor功能更强大,它包含三个成员变量

(1)Long类型的time,表示该任务将要被执行的具体时间

(2)Long类型period 表示任务执行的间隔周期

7.FutureTask简介

FutureTask实现了Future接口和Runnable接口,包含三种状态

(1)未启动.FutureTask.run()方法执行之前

(2)已启动.FutureTask.run()方法执行过程中

(3)已完成.FutureTask.run()方法执行完成之后

(4)FutureTask.cancel方法的调用情况

①未启动时调用,该任务永远不会被执行

②启动时调用,会中断该线程

③已完成时调用,会返回false,表示任务取消失败