本文查阅参考CSDN:
https://blog.csdn.net/cchengone/article/details/78135500
https://blog.csdn.net/ax8785r8C32nef593/article/details/78956865
java线程池实现类ThreadPoolExecutor
ThreadPoolExecutor可以构造一个线程池,就是装了一堆线程的容器。这些线程可以用来执行任务。其实按道理讲,我们可以自己创建一个线程来执行自己的任务。那么为啥要用线程池呢,是因为线程池装了一堆已经创建好了的线程,不需要我们自己再去创建,这节省了创建线程的资源,也保障了线程的高效利用。
任务管理主要方法如下:
上述方法中, execute() 和 submit() 方法在有空闲线程存在的情况下会立即调用该线程执行任务,区别在于 execute() 方法是忽略任务执行结果的,而submit() 方法则可以获取结果。
除此之外,ThreadPoolExecutor还提供了 execute() 和shutdownNow() 方法用于关闭线程池,区别在于shutdown() 方法在调用之后会将任务队列中的任务都执行完毕之后再关闭线程池,而shutdownNow() 方法则会直接关闭线程池,并且将任务队列中的任务导出到一个列表中返回。
除上述用于执行任务的方法外,ThreadPoolExecutor还提供了如下几个hook(钩子) 方法:
在ThreadPoolExecutor中这几个方法默认都是空方法,beforeExecute()会在每次任务执行之前调用,afterExecute()会在每次任务结束之后调用,terminated()方法则会在线程池被终止时调用。使用这几个方法的方式就是声明一个子类继承ThreadPoolExecutor,并且在子类中重写需要定制的钩子方法,最后在创建线程池时使用该子类实例即可。
a.相关参数
对于ThreadPoolExecutor的实例化,其主要有如下几个重要的参数:
corePoolSize: 线程池核心线程的数量;
maximumPoolSize: 线程池可创建的最大线程数量;
keepAliveTime: 当线程数量超过了corePoolSize指定的线程数,并且空闲线程空闲的时间达到当前参数指定的时间时该线程就会被销毁,如果调用过allowCoreThreadTimeOut(boolean value)方法允许核心线程过期,那么该策略针对核心线程也是生效的;
unit: 指定了keepAliveTime的单位,可以为毫秒,秒,分,小时等;
workQueue: 存储未执行的任务的队列;
threadFactory: 创建线程的工厂,如果未指定则使用默认的线程工厂;
handler: 指定了当任务队列已满,并且没有可用线程执行任务时对新添加的任务的处理策略;
b.调度策略
当初始化一个线程池之后,池中是没有任何用户执行任务的活跃线程的,当新的任务到来时,根据配置的参数其主要的执行任务如下:
若线程池中线程数小于corePoolSize指定的线程数时,每来一个任务,都会创建一个新的线程执行该任务,无论线程池中是否已有空闲的线程;
若当前执行的任务达到了corePoolSize指定的线程数时,也即所有的核心线程都在执行任务时,此时来的新任务会保存在workQueue指定的任务队列中;
当所有的核心线程都在执行任务,并且任务队列中存满了任务,此时若新来了任务,那么线程池将会创建新线程执行任务;
若所有的线程(maximumPoolSize指定的线程数)都在执行任务,并且任务队列也存满了任务时,对于新添加的任务,其都会使用handler所指定的方式对其进行处理。
ThreadPoolExcecutor线程池中,装的是多个Thread。
Runable,Callable是两个可以交给线程执行的任务,它俩有个区别就是,Callable对应的call方法可以自定义返回值,Runable的run方法没有返回值。
那么Future在这其中又扮演什么角色呢?ThreadPoolExcecutor的submit方法,可以传入Callable任务,也可以传入Runable任务,该方法可以返回Future对象。Future对象可以获取任务的完成情况,Future的isDone()方法可以获取当前任务是否执行完毕,该方法不会阻塞;get()方法可以获取任务执行结果的返回值,注意,get()方法是会阻塞的方法,也就是说当前任务在线程中没有运行完毕,只要程序调用了get()方法,会一直阻塞,等到线程运行完毕之后,才能继续执行下去。