java线程池的使用
背景
刷新缓存时会有线程被阻塞。为避免阻塞用户线程,把刷新线程放到后台执行。
当key过期,有新用户读取cache时,开启一个新线程去load数据库数据,用户线程返回老的值。
因此需要添加一个线程池来刷新cache。
线程池的学习与使用
ThreadPoolExecutor类
ThreadPoolExecutor类继承AbstractExecutorService类,并提供了四个构造器。
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue);
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory);
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler);
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler);
其中前三个构造器都是调用第四个构造器进行的初始化工作。
构造器各参数含义
- corePoolSize:核心池大小。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。
- maximumPoolSize:线程池最大线程数,表示在线程池中最多能创建多少个线程。
- keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0。
- unit:参数keepAliveTime的时间单位。
- workQueue:一个阻塞队列,用来存储等待执行的任务。
- threadFactory:线程工厂,主要用来创建线程。
- handler:表示当拒绝处理任务时的策略,有四种取值:
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
ThreadPoolExecutor类中重要方法
- execute(): 通过这个方法可以向线程池提交一个任务,交由线程池去执行。
- submit(): 这个方法也是用来向线程池提交任务的,但是它能够利用了Future来获取任务执行结果。
- shutdown(): 关闭线程池,使线程池处于SHUTDOWN状态,此时线程池不能接受新的任务,它会等待所有任务执行完毕。
- shutdownNow(): 关闭线程池,使线程池处于STOP状态,此时线程池不能接受新的任务,并且会去尝试终止正在执行的任务。
任务提交给线程池之后到被执行的过程
- 当前线程池中的线程数目小于corePoolSize时,每来一个任务,就会创建一个线程去执行这个任务。
- 当前线程池中的线程数目大于等于corePoolSize时,每来一个任务,会尝试将其添加到任务缓存队列当中。若添加成功,则该任务会等待空闲线程将其取出去执行;若添加失败(一般来说是任务缓存队列已满),则会尝试创建新的线程去执行这个任务。
- 当前线程池中的线程数目达到maximumPoolSize时,会采取任务拒绝策略进行处理。
- 线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数目不大于corePoolSize。
线程初始化
默认情况下,创建线程池之后,线程池中是没有线程的,需要提交任务之后才会创建线程。
如果需要线程池创建之后立即创建线程,可以采用下列两种方法预创建线程。
- prestartCoreThread():初始化一个核心线程。
- prestartAllCoreThreads():初始化所有核心线程。
最后执行线程会阻塞在getTask方法中的r = workQueue.take();
即等待任务队列中有任务。
任务缓存队列
任务缓存队列用来存放等待执行的任务,类型为BlockingQueue
- ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小。
- LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE。
- synchronousQueue:这个队列不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。
线程池容量的动态调整
- setCorePoolSize:设置核心池大小。
- setMaximumPoolSize:设置线程池最大能创建的线程数目大小。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
2019-03-11 第一次会议
2019-03-11 软件工程课程设计-项目初始