1. 场景描述
以前多线程也常用, 这次因需再页面上用到多线程, 如下图, 总结下, 有需要的朋友可以参考下.
2. 解决方案
2.1 线程池概念
线程池官方定义不说了, 通俗说下: 池子的概念, 事先 (预定义) 创建后, 后续的线程可以直接从池子中拿, 好处:
(1)来创建线程比较消耗资源, 不用重复创建;
(2)池子事先定义好, 避免无节制创建线程, 导致系统出现不可预测风险.
2.2 创建方式
采用 jdk 自带的线程池创建方式, jdk1.5 开始提供, 在 java.util.concurrent 的包下面.
表面上有两种创建方式, 其实一种.
(1)一种是采用 new ThreadPoolExecutor 进行创建;
- public ThreadPoolExecutor(int corePoolSize,
- int maximumPoolSize,
- long keepAliveTime,
- TimeUnit unit,
- BlockingQueue<Runnable> workQueue,
- ThreadFactory threadFactory,
- RejectedExecutionHandler handler) {
(2)一种是采用 Executors.newFixedThreadPool(3), 不过还是调用的 new ThreadPoolExecutor 进行的线程池创建, 赋值了几个默认参数而已.
- new ThreadPoolExecutor(nThreads, nThreads,
- 0L, TimeUnit.MILLISECONDS,
- new LinkedBlockingQueue<Runnable>());
2.3 参数含义
一共有 7 个参数:
(1)corePoolSize
核心线程数, 当有任务进来的时候, 如果当前线程数还未达到 corePoolSize 个数, 则创建核心线程, 核心线程有几个特点:
1, 当线程数未达到核心线程最大值的时候, 新任务进来, 即使有空闲线程, 也不会复用, 仍然新建核心线程; 2, 核心线程一般不会被销毁, 即使是空闲的状态, 但是如果通过方法 allowCoreThreadTimeOut(boolean value) 设置为 true 时, 超时也同样会被销毁; 3, 生产环境首次初始化的时候, 可以调用 prestartCoreThread() 方法来预先创建所有核心线程, 避免第一次调用缓慢;
(2)maximumPoolSize
除了有核心线程外, 有些策略是当核心线程完全无空闲的时候, 还会创建一些临时的线程来处理任务, maximumPoolSize 就是核心线程 + 临时线程的最大上限. 临时线程有一个超时机制, 超过了设置的空闲时间没有事儿干, 就会被销毁.
(3)keepAliveTime
这个就是上面两个参数里所提到的超时时间, 也就是线程的最大空闲时间, 默认用于非核心线程, 通过 allowCoreThreadTimeOut(boolean value) 方法设置后, 也会用于核心线程.
(4)unit
这个参数配合上面的 keepAliveTime , 指定超时的时间单位, 秒, 分, 时等.
(5)workQueue
等待执行的任务队列, 如果核心线程没有空闲的了, 新来的任务就会被放到这个等待队列中.
(6)threadFactory
它是一个接口, 用于实现生成线程的方式, 定义线程名格式, 是否后台执行等等, 可以用 Executors.defaultThreadFactory() 默认的实现即可, 也可以用 Guava 等三方库提供的方法实现, 如果有特殊要求的话可以自己定义. 它最重要的地方应该就是定义线程名称的格式, 便于排查问题了吧.
(7)handler
当没有空闲的线程处理任务, 并且等待队列已满(当然这只对有界队列有效), 再有新任务进来的话, 就要做一些取舍了, 而这个参数就是指定取舍策略的, 有下面四种策略可以选择:
ThreadPoolExecutor.AbortPolicy: 直接抛出异常, 这是默认策略;
ThreadPoolExecutor.DiscardPolicy: 直接丢弃任务, 但是不抛出异常.
ThreadPoolExecutor.DiscardOldestPolicy: 丢弃队列最前面的任务, 然后将新来的任务加入等待队列
ThreadPoolExecutor.CallerRunsPolicy: 由线程池所在的线程处理该任务, 比如在 main 函数中创建线程池, 如果执行此策略, 将有 main 线程来执行该任务
2.4 测试验证
2.4.1 测试线程
- package com.yutong.laowang.test;
- public class ThreadTest extends Thread{
- @Override
- public void run() {
- System.out.println("软件老王:" +Thread.currentThread().getName());
- }
- }
2.4.2 Executors 创建
- private static void test4() {
- Executor mExecutor = Executors.newFixedThreadPool(3);
- for (int i = 0; i <10; i++) {
- Thread thread = new ThreadTest();
- mExecutor.execute(thread);
- }
- }
结果:
软件老王: pool-2-thread-2
软件老王: pool-2-thread-3
软件老王: pool-2-thread-1
软件老王: pool-2-thread-3
软件老王: pool-2-thread-2
软件老王: pool-2-thread-1
软件老王: pool-2-thread-3
软件老王: pool-2-thread-2
软件老王: pool-2-thread-3
软件老王: pool-2-thread-1
2.4.3 ThreadPoolExecutor 创建
- private static void test3() {
- int poolSize = 5;
- int queueSize = 100;
- ExecutorService executorService = new ThreadPoolExecutor(poolSize, poolSize, 0L, TimeUnit.SECONDS,
- new ArrayBlockingQueue<Runnable>(queueSize), new ThreadPoolExecutor.AbortPolicy());
- for (int i=0;i<10;i++) {
- executorService.submit(new ThreadTest());
- }
- }
结果:
软件老王: pool-2-thread-1
软件老王: pool-2-thread-3
软件老王: pool-2-thread-4
软件老王: pool-2-thread-2
软件老王: pool-2-thread-5
软件老王: pool-2-thread-5
软件老王: pool-2-thread-1
软件老王: pool-2-thread-2
软件老王: pool-2-thread-2
软件老王: pool-2-thread-1
2.4.4 线程停用
thread.interrupt();
--- 有时候不一定能执行成功, 一般会结合判断使用, 软件老王, 例如:
在 ThreadTest 类中进行判断, 默认为 false, 当满足条件下为 true, 停用线程.
public volatile boolean exit = false;
I'm 「软件老王」, 如果觉得还可以的话, 关注下呗, 后续更新秒知! 欢迎讨论区, 同名公众号留言交流!
来源: https://www.cnblogs.com/ruanjianlaowang/p/12014490.html