线程池
- 可以做什么
- 怎么创建
先了解下线程的生命周期
解释:
- 新建:线程创建但未被启动
- 可运行:线程处于可运行的状态,当该线程有cpu的时间片后就可以执行
- 睡眠:线程进入计时等待,等待一段指定的时间,可以通过wait()方法,sleep方法,join()方法等方式
- 等待:线程进入等待模式,直到其他线程唤醒它,可以通过wait()方法,join()方法或lock类的条件等待方法进入该状态
- 阻塞:线程执行被阻止执行,因为它正在等待监视器锁定,其他的线程正在占用所用的锁定1,因此线程被阻塞
- 线程完成了其任务,或因为异常或其他原因进而终止运行
简单总结: 创建线程的方法:(留个记录)
- 通过继承thread类创建
- 通过实现runnable接口
- 使用匿名内部类
- 使用lambda表达式
线程池
线程池:简单的来说就是创建好的多个线程放在的一起的地方,这个地方就是线程池,我们使用线程的时候直接可以去这个地方拿取线程
通过上面线程的线程的周期可以知道线程池的好处:避免重复的创建线程和关闭线程所浪费的系统资源
创建线程池的方法
- 通过new ThreadPoolExecutor的方式
- 通过spring的ThreadPoolTaskExecutor方式,(建议使用这个)
以ThreadPoolExecutor类的有参构造有七个参数的方法为探讨:
- int corePoolSize:核心线程数
- nt maximumPoolSize:最大线程数
- long keepAliveTime:临时线程的有效时间数值
- TimeUnit unit:有效时间的单位(时,分,秒)
- BlockingQueue workQueue:线程池使用的缓冲队列,指的是被提交的任务但尚未被执行的任务
- ThreadFactory threadFactory:创建线程的方法,一般默认即可
- RejectedExecutionHandler handler:拒绝策略,当任务太多来不及处理,如何拒绝任务
注意事项:
- 核心线程数的大小,参考任务的类型和系统资源进行合理配置,过大,导致浪费系统资源,过小,导致任务队列等待执行,影响系统的响应性能
- 任务队列的选择,参考任务的执行时长和任务的多少,有几个常见的队列,任务多执行时间短无界队列,任务少时间长的可以选择有界队列或优先级队列
- 线程池的拒绝策略常见四个,抛出异常,丢弃任务,丢弃最早的任务,调用者运行任务这个几个策略,具体参考业务的需要
- 线程的声明周期:创建初始化,设置参数,执行任务时需要提交任务到线程池,关闭线程时调用线程池的方法shutdown()或shutdownNow()方法关闭现场,并等待所有的任务完成,正确的管理线程池的声明周期可以避免资源的泄露和线程阻塞的问题
- 线程的安全:多个任务并发执行时,可能涉及到公共资源的访问,需要使用合适的同步机制来保证线程的安全
线程池的工作原理
拒绝策略
- ThreadPoolExecutor.AbortPolicy(默认):当线程池无法处理新的任务时,会抛出RejectedExecutionException异常。
- ThreadPoolExecutor.CallerRunsPolicy:当线程池无法处理新的任务时,会将任务返回给调用者执行。也就是说,如果线程池被主线程调用,主线程会自己执行任务。
- ThreadPoolExecutor.DiscardPolicy:当线程池无法处理新的任务时,会直接丢弃这个任务,不会有任何提示或处理。
- ThreadPoolExecutor.DiscardOldestPolicy:当线程池无法处理新的任务时,会先尝试将最早的任务从队列中删除,然后再尝试执行新的任务。
除了以上四种常见的拒绝策略,也可以自定义拒绝策略,实现RejectedExecutionHandler接口,并重写rejectedExecution()方法来定义自己的处理逻辑。