关于多线程:Java线程池的使用及工作原理

78次阅读

共计 2377 个字符,预计需要花费 6 分钟才能阅读完成。

前言

在日常开发过程中总是以单线程的思维去编码,没有思考到在多线程状态下的运行状况。由此引发的后果就是申请过多,利用无奈响应。为了解决申请过多的问题,又衍生出了线程池的概念。通过“池”的思维,从而正当的解决申请。本文记录了 Java 中线程池的应用及工作原理,如有谬误,欢送斧正。

什么是线程池?

线程池是一种用于实现计算机程序并发执行的软件设计模式。线程池保护多个线程,期待由调度程序分配任务以并发执行,该模型进步了性能,并防止了因为为短期工作频繁创立和销毁线程而导致的执行提早。

线程池要解决什么问题?

说到线程池就肯定要从线程的生命周期讲起。

](/img/bVcSinY)

从图中能够理解无论工作执行多久,每个线程都要经验从生到死的状态。而应用线程池就是为了防止线程的反复创立,从而节俭了线程的 NewRunnableRunningTerminated 的工夫;同时也会复用线程,最小化的节俭系统资源,于此同时进步了响应速度。

线程池的应用

线程池的创立

应用 ThreadPoolExecutor 并配置 7 个参数实现线程池的创立

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
  • corePoolSize:线程池中外围线程的最大值
  • maximumPoolSize:线程池中最大线程数
  • keepAliveTime:非核心线程闲暇的存活工夫大小
  • unit:keepAliveTime 的单位,罕用的有秒、分钟、小时等
  • workQueue:阻塞队列类型
  • threadFactory:线程工厂,用于配置线程的名称,是否为守护线程等
  • handler:线程池的回绝策略

罕用阻塞队列

ArrayBlockingQueue

底层基于数组的实现的有界阻塞队列

LinkedBlockingQueue

底层基于单链表的阻塞队列,可配置容量,不配置容量默认为Integer.MAX_VALUE

线程工厂

在《阿里巴巴 Java 开发手册》中强制要求指定线程的名称
](/img/bVcSinX)

因为工作是应用 hutool 比拟多,外面也蕴含对 ThreadFactory 的封装,能够很不便的指定名称

ThreadFactory threadFactory = ThreadFactoryBuilder.create().setNamePrefix("myThread-").build();

回绝策略

当线程池内工作线程数大于 maximumPoolSize 时,线程就不再接受任务,执行对应的回绝策略;目前反对的回绝策略有四种:

  1. AbortPolicy(默认):抛弃工作并抛出 RejectedExecutionException 异样
  2. CallerRunsPolicy:由调用者解决
  3. DiscardOldestPolicy:抛弃队列中最后面的工作,并从新入队列
  4. DiscardPolicy:抛弃工作但不抛出异样

线程池的执行逻辑

// 创立线程工厂
ThreadFactory threadFactory = ThreadFactoryBuilder.create().setNamePrefix("myThread-").build();
// 创立线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100), threadFactory, new ThreadPoolExecutor.AbortPolicy());

execute()办法

// 组合值;保留了线程池的工作状态和工作线程数
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    public void execute(Runnable command) {
        // 工作为空 抛出 NPE
        if (command == null)
            throw new NullPointerException();
        // 获取线程池状态
        int c = ctl.get();
        // 如果工作线程数小于外围线程数就创立新线程
        if (workerCountOf(c) < corePoolSize) {if (addWorker(command, true))
                return;
            c = ctl.get();}
        // 如果线程池处于 Running 状态,就把工作放在队列尾部
        if (isRunning(c) && workQueue.offer(command)) {
            // 从新查看线程池状态
            int recheck = ctl.get();
            // 如果线程池不是 Running 状态,就移除方才增加的工作,并执行回绝策略
            if (! isRunning(recheck) && remove(command))
                reject(command);
            // 是 Running 状态,就增加线程
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        // 增加工作失败,执行回绝策略
        else if (!addWorker(command, false))
            reject(command);
    }
// addWorker()实现线程的创立

执行流程

参考文章:

  1. 面试必备:Java 线程池解析 (juejin.cn)
  2. 别再说你不懂线程池——做个优雅的攻城狮 (juejin.cn)
  3. Java 线程池实现原理及其在美团业务中的实际 – 美团技术团队 (meituan.com)

浏览原文

正文完
 0