龙空技术网

详解Java线程池的使用及工作原理

Java架构师知识 161

前言:

现时姐妹们对“java如何使用线程池”大体比较注重,小伙伴们都需要学习一些“java如何使用线程池”的相关知识。那么小编也在网络上网罗了一些关于“java如何使用线程池””的相关知识,希望朋友们能喜欢,小伙伴们一起来学习一下吧!

在日常开发过程中总是以单线程的思维去编码,没有考虑到在多线程状态下的运行状况。由此引发的结果就是请求过多,应用无法响应。为了解决请求过多的问题,又衍生出了线程池的概念。通过“池”的思想,从而合理的处理请求。本文记录了Java中线程池的使用及工作原理,如有错误,欢迎指正。总结了一张Java多线程图谱分享给大家:

什么是线程池?

线程池是一种用于实现计算机程序并发执行的软件设计模式。线程池维护多个线程,等待由调度程序分配任务以并发执行,该模型提高了性能,并避免了由于为短期任务频繁创建和销毁线程而导致的执行延迟。

线程池要解决什么问题?

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

从图中可以了解无论任务执行多久,每个线程都要经历从生到死的状态。而使用线程池就是为了避免线程的重复创建,从而节省了线程的New至Runnable, Running至Terminated的时间;同时也会复用线程,最小化的节省系统资源,于此同时提高了响应速度。

线程池的使用线程池的创建

使用ThreadPoolExecutor并配置7个参数完成线程池的创建

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)

corePoolSize

线程池中核心线程的最大值

maximumPoolSize

线程池中最大线程数

keepAliveTime

非核心线程空闲的存活时间大小

unit

keepAliveTime的单位,常用的有秒、分钟、小时等

workQueue

阻塞队列类型

threadFactory

线程工厂,用于配置线程的名称,是否为守护线程等

handler

线程池的拒绝策略

常用阻塞队列ArrayBlockingQueue

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

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

线程工厂

在《阿里巴巴Java开发手册》中强制要求指定线程的名称

由于工作是使用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()完成线程的创建
执行流程

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

最后

另外还整理成了40多套PDF文档:全套的Java面试宝典手册,“性能调优+微服务架构+并发编程+开源框架+分布式”等七大面试专栏,包含Tomcat、JVM、MySQL、SpringCloud、SpringBoot、Dubbo、并发、Spring、SpringMVC、MyBatis、Zookeeper、Ngnix、Kafka、MQ、Redis、MongoDB、memcached等等。如果你对这个感兴趣,小编可以免费分享。

资料获取方式:关注小编+转发文章+私信【面试题】获取上述资料~

重要的事情说三遍,转发+转发+转发,一定要记得转发哦!!

标签: #java如何使用线程池