Java线程池
一、Executors创建线程池
newFixedThreadPool:固定指定线程数的线程池
newSingleThreadExector:只有一个线程的线程池
newCachedThreadPool:不限线程数上限的线程池,任何提交的任务都将立即执行
newScheduledThreadPool:定时周期性执行的线程池
任务类型:
线程执行的任务,分为两种:1.实现Runnable接口;2.实现Callable接口;
区别:
Runnable:void Runnable.run();
Callable:T Callable.call() throws Exception
说明:Runnable没有返回值且不能抛出异常,Callable可以有返回值允许抛出异常。
线程池提交任务的3种方式:
返回结果:Future<T> submit(Callable<T> task);
返回空:Future<?> submit(Runnable task),该方法有返回值,恒为空null
不返回结果:void execute(Runnable command);
二、优化创建线程池
避免直接使用Executors.newXXXThreadPool()的快捷方法创建线程池,因为Executors默认使用无界队列,容易导致OOM(内存不足)。所以需要明确指定使用有界队列(指定队列最大长度),且应当指明拒绝任务时发生的行为(策略)
new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler);
corePoolSize(核心线程数):线程池中常驻的线程数,线程池初始化时默认是没有线程的,当任务来临时才开始创建线程去执行任务。
maximumPoolSize(最大线程数):在核心线程数的基础上,当workQueue队列填满时会创建多于corePoolSize且总线程数不超过maxPoolSize的线程
keepAliveTime(非核线程空闲时长):当非核心线程的空闲时间该值时,会被自动终止回收掉。不过当corePoolSize=maxPoolSize时,keepAliveTime参数也就不起作用了(因为不存在非核心线程);
unit:keepAliveTime的时间单位
workQueue(工作队列):用于保存任务的队列,可以为无界、有界、同步移交三种队列类型之一,当池子里的工作线程数大于corePoolSize时,这时新进来的任务会被放到队列中
SynchronousQueue(同步移交队列):队列不作为任务的缓冲方式,可以简单理解为队列长度为零
LinkedBlockingQueue(无界队列):队列长度不受限制,当请求越来越多时(任务处理速度跟不上任务提交速度造成请求堆积)可能导致内存不足
ArrayBlockintQueue(有界队列):队列长度受限,当队列满了就需要创建多余的线程来执行任务
threadFactory(线程工厂):创建线程的工厂类,默认使用Executors.defaultThreadFactory(),也可以使用guava库的ThreadFactoryBuilder来创建
handler(拒绝策略):线程池满载(队列数已满且线程数=maximunPoolSize)的时候,执行的拒绝接收任务的策略,取值有AbortPolicy、DiscardPolicy、DiscardOldestPolicy、CallerRunsPolicy
AbortPolicy:默认方式,拒绝新任务并抛出RejectedExecutionException异常
DiscardPolicy:什么也不做,直接忽略
DiscardOldestPolicy:废弃队列中最老任务,接收当前任务。
CallerRunsPolicy:直接由提交任务者执行这个任务
三、线程池的关闭
shutdownNow():立即关闭线程池,正在执行中的及队列中的任务会被中断,同时该方法会返回被中断的队列中的任务列表
shutdown():平滑关闭线程池,正在执行中的及队列中的任务能执行完成,后续进来的任务会被执行拒绝策略
isTerminated():当正在执行的任务及对列中的任务全部都执行(清空)完就会返回true
四、线程池中线程数量定义
线程池中,最核心的两个参数:核心线程数(corePoolSize)和最大线程数(maximumPoolSize)的数量设置
核心线程数:
CPU密集型任务:
比如加解密、压缩、计算等,需大量消耗CPU资源的任务,最佳的线程数为 CPU 核心数的 1~2 倍
耗时I/O型任务:
比如数据库读取、文件读写、网络通信等,不会特别消耗CPU资源,但是总体耗时较多的任务
线程数=CPU核心数*(1+平均等待时间/平均工作时间)
单线程平均工作时间长,线程数相应减少。单线程平均工作时间越短,线程数相应增加。
五、线程工厂
通过线程工厂给每个创建出来的线程设置极富业务含义的名字,实现业务上的肉眼区分
通过第三方jar实现
Spring下的CustomizableThreadFactory:Spring框架提供
ThreadFactory springFactory = new CustomizableThreadFactory("spring-pool-");
ExecutorService exec = new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(100), springFactory);
guava下的ThreadFactoryBuilder:Google 开源框架guava提供
ThreadFactory guavaFactory = new ThreadFactoryBuilder().setNameFormat("guava-pool-").build();
ExecutorService exec = new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(100), guavaFactory);
Apache下的commons-lang3 BasicThreadFactory:Apache提供。
ThreadFactory apacheFactory = new BasicThreadFactory.Builder().namingPattern("apache-pool-").build();
ExecutorService exec = new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(100), apacheFactory);
自定义ThreadFactory
实现ThreadFactory接口,赋予线程名称属性。