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)方法。

CustomThreadFactory

Executors

虽然使用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,线程池中只有一个重用线程

推荐阅读更多精彩内容