在java编程中,需要应用多线程的情况还是比较多的,多线程程序编写也有很多方式,最原始的方法,直接new Thread重写run方法,或者是实现Runable。这两种方式本质都是直接new Thread。效率和资源利用率比较低。下面我说一下比较推荐的方式。
A thread pool should be created by ThreadPoolExecutor
rather than Executors
. These would make the parameters of the thread pool understandable. It would also reduce the risk of running out of system resource.
上面这段英文摘抄自阿里巴巴的java编写规范P3C文档中关于多线程的规范建议。翻译一下:
线程池的创建应当使用ThreadPoolExecutor
而不是Executors。这样可以使线程池创建的参数更容易理解,而且也会降低程序运行发生意外的风险。
既然阿里巴巴关于多线程使用都给出具体类名了,我们也就无须质疑这个的好用性了,下面我们就研究一下ThreadPoolExecutor
这个线程池的使用和参数设置意义基本就可以了。
这个类有很多构造参数,不过构造参数基本都是对类属性的初始化,我们只要搞明白每个属性的意义就基本理解了这个线程池的使用。
Core and maximum pool sizes
一个ThreadPoolExecutor
会自动适应线程池内的线程数量,最小Core ,最大maximum ,当一个新的任务通过 execute方法提交执行时。如果当前线程池内的线程数量小于Core,这时候即使有其他线程处于空闲状态,也会创建一个新的线程执行任务。如果当前线程池内的线程数量大于Core,但是小于maximum ,这时候只有当所有的queue(线程排队策略,一个属性,后面会介绍这个东西)都满队列的情况下才会创建新的线程。当线程池数量等于maximum ,并且所有的queue都满了,就会用到的拒绝策略RejectedExecutionHandler(一个属性,后面会介绍这个东西)。
keepAliveTime
如果当前的线程池数量大于Core,多出的线程在空闲这个时间后会被停止。这个参数的意义是当一个线程执行完任务后,后面的任务还没到达,可以让这个线程等待一定时间在销毁,可以执行后面的任务,而不需要新建线程。
线程queue
Unbounded queues 举例:
LinkedBlockingQueue
一个线程无限排队提交过来的任务,这种排队策略线程池一般最多只有Core,因为新提交的任务会在已有的线程中排队执行,而不会新建线程。
Bounded queues 举例:
ArrayBlockingQueue 一个有上限数量的排队线程,当线程数量大于Core时,新提交的任务会在queue里面排队执行,当所有的线程queue排满时会创建新的线程。
RejectedExecutionHandler
拒绝策略
AbortPolicy,直接报错,,,
CallerRunsPolicy,在提交任务的线程自己执行。
DiscardPolicy,新提交的任务直接扔掉,执行失败。
DiscardOldestPolicy
, 线程中,排序靠前的放弃执行,当有空余线程时在执行,当然也会应为新的任务提交再次失败。
了解上面那么多基本就可以实战使用了,当然还有很有其他属性,策略,甚至很多策略都是可以通过集成重写的方式自己实现,这就是高阶一点的了,对于一般的需求,知道这些基本够了。