Java线程池(二)
接下来三篇文章主要围绕Java线程池的三个方面进行编写
第一个方面Exectutor,该类是Java线程池的顶层抽象类
第二个方面:讲述Java线程池中的各种线程池
第三个方面:讲述Future和FutureTask,线程的返回结果处理类
ThreadPoolExecutor通常使用工厂类Executors来创建。Executors可以创建3种类型的ThreadPoolExecutor:SingleThreadPool、FixedThreadPool和CachedThreadPool。
ScheduledThreadPoolExecutor通常使用工厂类Executors来创建。Executors可以创建2种类型的ScheduledThreadPoolExecutor:SingleScheduledThreadPoo和ScheduledThreadPool
注意:使用Executors来创建线程池,失去了线程池的灵活性,而且存在一定的隐患,根据阿里巴巴规范插件的提示,使用Executors来创建线程池存在资源耗尽的可能,因为使用Executors来创建线程池默认的最大容量是Integer.Max,也就是Integer的最大值作为线程池的最大容量,这样在程序中可能出现错误,导致创建了Integer.Max个线程,存在内存溢出的风险。所以在熟练掌握线程池原理后可以使用ThreadPoolExecutor根据添加不同的参数创建不同类型的线程池。
ThreadPoolExecutor
了解ThreadPoolExecutor可以查看《Java线程池原理》
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
下面解释构造函数的参数含义
corePoolSize:核心线程数量
maximumPoolSize:最大线程数量;
workQueue:等待队列,当任务提交时,如果线程池中的线程数量大于等于corePoolSize的时候,把该任务封装成一个Worker对象放入等待队列;
keepAliveTime:线程池维护线程所允许的空闲时间。当线程池中的线程数量大于corePoolSize的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了keepAliveTime;
TimeUnit :时间级别
threadFactory:它是ThreadFactory类型的变量,用来创建新线程。
handler:它是RejectedExecutionHandler类型的变量,表示线程池的饱和策略。如果阻塞队列满了并且没有空闲的线程,这时如果继续提交任务,就需要采取一种策略处理该任务。
ThreadFactory:定义线程池中创建的线程,如线程名称,优先级等,可以几次ThreadFactory重新newThread(Runnable r)方法。
CustomThreadFactoryExecutors
虽然使用Executors创建线程池存在一定的风险,但是在有些不是很复杂的场景,合理使用Executors还是可行的。下面使用Executors来创建不同类型的线程池
固定数量线程池(newFixedThreadPool)
创建使用固定线程数的FixedThreadPool,适用于为了满足资源管理的需求,而需要限制当前线程数量的应用场景,它适用于负载比较重的服务器。
Executors构造newFixedThreadPool方式
查看源码
corePoolSize = maximumPoolSize =初始化的参数
workQueue:使用无界队列LinkedBlockingQueue链表阻塞队列
keepAliveTime = 0 由于使用无界队列LinkedBlockingQueue作为缓存队列,所以当corePoolSize满后,后面添加的线程任务都会添加到LinkedBlockingQueue中去,所以maximumPoolSize 就失去了意义,这样也就没有必要设置空闲时间
使用无界队列的影响,这也是为什么使用Eexcutors来创建线程池存在一定风险的原因
1)当线程池中的线程数达到corePoolSize后,新任务将在无界队列中等待,因此线程池中的线程数不会超过corePoolSize。
2)使用无界队列时maximumPoolSize将是一个无效参数。
3)使用无界队列时keepAliveTime将是一个无效参数。
4)由于使用无界队列,运行中的FixedThreadPool(未执行方法shutdown()或shutdownNow())不会拒绝任务(不会调用RejectedExecutionHandler.rejectedExecution方法)。
代码实例
结果:
为什么线程名称会重复:这正是线程池的原理,因为线程池会重复利用已创建的线程,当一个任务Runnable被挂载到线程池中的一个线程,这个任务执行完毕后,会有另一个任务继续挂载到这个线程上面,所以会出现线程名称重复。
单例线程池(newSingleThreadExecutor)
适用于需要保证顺序地执行各个任务;并且在任意时间点,不会有多个线程是活动的应用场景。
Executors构造newSingleThreadExecutor方式
源代码
corePoolSize = maximumPoolSize =1 由于是单例线程池,所以线程池中是有一个重用的线程
workQueue:使用无界队列LinkedBlockingQueue链表阻塞队列
keepAliveTime:0 原因上面已经阐述
代码实例
结果
只有一个可重用的线程,任务的执行顺序和添加顺序一致
缓存线程池(newCachedThreadPool)
创建一个会根据需要创建新线程的,适用于执行很多的短期异步任务的小程序,或者是负载较轻的服务器。
Executors构造newCachedThreadPool方式
源代码
corePoolSize:0 表示线程池中没有核心线程,都是非核心线程
maximumPoolSize :线程池容量Integer最大值
keepAliveTime:60秒 由于没有核心线程的存在,线程池中创建的线程都是非核心线程,所以设置空闲时间60秒,当非核心线程60秒后没有被重用,将会被销毁,如果没有线程提交给该线程池,超过空闲时间,该线程池就没有非空闲线程,那么该线程池也就不会消耗过多的资源,
workQueue:SynchronousQueue是一个不存储元素的阻塞队列。每一个put操作必须等待一个take操作,否则不能继续添加元素。
代码实例
结果
定时线程池(newScheduledThreadPool)
它主要用来在给定的延迟之后运行任务,或者定期执行任务,例如定时轮询数据库中的表的数据
Executors构造newScheduledThreadPool方式
源代码
workQeueu:delayWorkQueue,使用延迟队列作为缓存队列
任务提交方式
schedule(Callable<E> callable, long delay, TimeUnit unit);
callable:提交Callable或者Runnable任务
delay:延迟时间
unit:时间级别
该方法表示在给定的delay延迟时间后执行一次,有返回结果
scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);
command:提交Runnable任务
initialDelay:初始延迟时间
period:表示连个任务连续执行的时间周期,第一个任务开始到第二个任务的开始,包含了任务的执行时间
unit:时间级别
该方法在initialDelay时间后开始周期性的按period时间间隔执行任务
scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit);
command:提交Runnable任务
initialDelay:初始延迟时间
delay:表示延迟时间 第一个任务结束到第二个任务开始的时间间隔
unit:时间级别
代码实例
结果
单例延迟线程池(newSingleThreadScheduledExecutor)
Executors构造newSingleThreadScheduledExecutor方式
源代码
corePoolSize :1由于是单例线程池,所以核心线程为1,线程池中只有一个重用线程