Java线程池八股及实现过程

 更新时间:2026年01月30日 10:27:00   作者:小楼v  
线程池的八股包括线程池的核心参数、执行原理、拒绝策略、阻塞队列、核心线程数的确定、线程池种类、不建议使用Executors创建线程池的原因、ThreadLocal的理解及ThreadLocal的内存泄露问题,展示了线程池的运行机制,本文介绍Java线程池八股及实现,感兴趣的朋友一起看看吧

线程池的八股

1、说一下线程池的核心参数?

ThreadPoolExecutor线程池的核心参数有7个,1.核心线程数目(corePoolSize)、2.最大线程数目(maximumPoolSize),最大线程数目=核心线程+临时线程、3.生存时间(keepAliveTime),是临时线程的生存时间,生存时间内没有新任务,临时线程会释放、4.时间单位(unit)、5.阻塞队列(workQueue),当核心线程满了之后新来的任务会进入队列排队,队列满后会创建临时线程执行任务、6.线程工厂(threadFactory),定制线程对象的创建,例如设置线程名字,是否是守护线程等、7.拒绝策略(handler),当所有线程都在忙,workQueue也放满时,就会触发拒绝策略。

2、线程池的执行原理?

首先提交任务会判断核心线程是否已满,如果没满就正常工作,如果已满的话就会判断这个阻塞队列是否已满,如果阻塞队列没满就放入阻塞队列等待,如果阻塞队列也满了,就判断线程数是否小于最大线程数,如果小于就创建临时线程,这时临时线程默认是来执行新来的任务,而不是执行从阻塞队列中取出的任务来执行,如果不小于,那就走拒绝策略。

3、拒绝策略有哪些?

拒绝策略有4种,1.AbortPolicy,直接抛异常,默认策略。2.CallerRunsPolicy,使用调用者所在线程来执行任务,如主线程main。3.DiscardOldestPolicy,丢弃阻塞队列最靠前的任务,来执行当前任务。4.DiscardPolicy,直接丢弃任务。

4、线程池中有哪些常见的阻塞队列?

有4种常见的阻塞队列,1.ArrayBlockingQueue,基于数组的有界阻塞队列,FIFO。2.LinkedBlockingQueue,基于链表的有界阻塞队列,FIFO。3.DelayedWorkQueue,优先级阻塞队列,保证每次出任务的是执行时间最靠前的任务。4.SynchronousQueue,不存储元素的阻塞队列,每次插入操作都要等待一个移除操作。

5、ArrayBlockingQueue和LinkedBlockingQueue有什么区别?

LinkedBlockingQueue默认是无界的,支持有界,ArrayBlockingQueue强制有界。LinkedBlockingQueue底层是链表,ArrayBlockingQueue底层是数组。LinkedBlockingQueue是懒惰的,创建节点时添加数据,入队会生成新Node,ArrayBlockingQueue是提前初始化Node数组,Node需要是提前创建好的。LinkedBlockingQueue是两把锁,链表的头和尾加锁,ArrayBlockingQueue是全部一把锁,一般平时常用的就是LinkedBlockingQueue。

6、如何确定核心线程数?

分IO密集型任务和CPU密集型任务,注:N为CPU核数。如果并发不高、执行时间长的话,设置为IO密集型任务核心线程数就是2N+1;设置为CPU密集型任务核心线程数就是N+1。如果高并发、任务执行时间短的话核心线程数就是N+1,减少线程上下文切换。

7、线程池的种类有哪些?

常见的有4种,1.创建使用固定线程数的线程池,就是核心线程数和最大线程数一样,没有临时线程,阻塞队列是LinkedBlockingQueue。2.单线程化的线程池,核心线程数和最大线程数都是1,阻塞队列是LinkedBlockingQueue。3.可缓存线程池,核心线程数为0,只有临时线程数,阻塞队列是SynchronousQueue,不存储元素的阻塞队列,每次插入操作都要等待一个移除操作。3.提供了“延迟”和“周期执行”功能的ThreadPoolExecutor,可以执行延迟任务的线程池,支持定时及周期性完成任务。

8、为什么不建议使用Executors创建线程池?

可能会堆积大量的请求,创建大量的对象,导致OOM(内存溢出)。

9、谈谈你对ThreadLocal的理解?

为每个线程都分配一个独立的线程副本,让多个线程只操作自己内部的值,解决了并发访问冲突问题,同时实现了线程内的资源共享。

10、你知道ThreadLocal内存泄露问题吗?

线程内都维护了ThreadLocalMap成员变量,用来存储资源对象,调用set()方法,以ThreadLocal作为key,资源对象作为value,放入ThreadLocalMap集合中,然后调用get()方法到线程中查找自己key关联的资源。然后呢,ThreadLocal有用到弱引用和强引用,弱引用的内存会被回收,而强引用不会被回收,积累满了就可能发生内存泄露问题,可以使用remove()移除当前线程资源来解决内存泄露问题。

线程池的实现

配置类:

@Configuration
public class ThreadPoolExecutorConfig {
    @Bean
    public ThreadPoolExecutor threadPoolExecutor() {
        // 创建线程工厂
        ThreadFactory threadFactory = new ThreadFactory() {
            // 初始化线程数为1
            private int count =1;
            @Override
            public Thread newThread(@NotNull Runnable r) {
                Thread thread = new Thread(r);
                thread.setName("线程" + count);
                count++;
                return thread;
            }
        };
        return new ThreadPoolExecutor(2, 3, 1000, TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), threadFactory);
    }
}

演示线程池工作流程(核心线程数:2;最大线程池:3;阻塞队列可存放任务:4):

/**
 * 线程池队列测试接口
 */
@RestController
@RequestMapping("/queue")
@Slf4j
public class QueueController {
    @Resource
    private ThreadPoolExecutor threadPoolExecutor;
// 核心线程数:2;最大线程池:3;阻塞队列可存放任务:4
    @GetMapping("/add")
    public void add(String taskName){
        // 创建任务
        CompletableFuture.runAsync(()->{
            log.info("当前线程:" + Thread.currentThread().getName() + "," + "正在执行任务" + taskName);
            try {
                Thread.sleep(1000000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        },threadPoolExecutor);
    }
    @GetMapping("/get")
    public String get(){
        Map<String, Object> map = new HashMap<>();
        long taskCount = threadPoolExecutor.getTaskCount();
        map.put("任务总数", taskCount);
        long completedTaskCount = threadPoolExecutor.getCompletedTaskCount();
        map.put("已完成任务数", completedTaskCount);
        int activeCount = threadPoolExecutor.getActiveCount();
        map.put("正在执行任务的线程数", activeCount);
        int size = threadPoolExecutor.getQueue().size();
        map.put("队列长度", size);
        return JSONUtil.toJsonStr(map);
    }
}

首先获取到当前状态情况:

以此类推发送任务2后执行情况如下:

两个核心线程占满,按预期接着发送任务应该放到队列中,队列长度+1:

因为我们阻塞队列可以存放4个任务,以此类推发满4个队列长度后再看看效果:

发送到任务7,也就是阻塞队列占满4个后再发送一个任务:

任务总数为7,队列长度依旧为4,可以发现临时线程默认是来执行新来的任务,而不是执行从阻塞队列中取出的任务来执行。

接着再发送一个任务,由于当前线程数=最大线程数,并且队列也满,预期应该是走默认拒绝策略:

符合预期。

到此这篇关于Java线程池八股及实现的文章就介绍到这了,更多相关Java线程池八股内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • m1 Mac设置多jdk版本并动态切换的实现

    m1 Mac设置多jdk版本并动态切换的实现

    本文主要介绍 Mac 下如何安装 JDK 并且多版本如何切换,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • Java SpringBoot开发小技巧详解

    Java SpringBoot开发小技巧详解

    这篇文章主要介绍了浅谈SpringBoot项目如何让前端开发提高效率(小技巧),主要介绍了Swagger和Nginx提高效率的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • 深入介绍Java对象初始化

    深入介绍Java对象初始化

    本文对Java如何执行对象的初始化做一个详细深入地介绍。有需要的小伙伴们可以参考。
    2016-07-07
  • 如何通过一个注解实现MyBatis字段加解密

    如何通过一个注解实现MyBatis字段加解密

    用户隐私很重要,因此很多公司开始做数据加减密改造,下面这篇文章主要给大家介绍了关于如何通过一个注解实现MyBatis字段加解密的相关资料,需要的朋友可以参考下
    2022-02-02
  • Mybatis中动态SQL,if,where,foreach的使用教程详解

    Mybatis中动态SQL,if,where,foreach的使用教程详解

    MyBatis的动态SQL是基于OGNL表达式的,它可以帮助我们方便的在SQL语句中实现某些逻辑。这篇文章主要介绍了Mybatis中动态SQL,if,where,foreach的使用教程,需要的朋友可以参考下
    2017-11-11
  • Java堆&优先级队列示例讲解(上)

    Java堆&优先级队列示例讲解(上)

    这篇文章主要通过示例详细为大家介绍Java中的堆以及优先级队列,文中的示例代码讲解详细,对我们了解java有一定帮助,需要的可以参考一下
    2022-03-03
  • SpringBoot+Tess4j实现牛的OCR识别工具的示例代码

    SpringBoot+Tess4j实现牛的OCR识别工具的示例代码

    这篇文章主要介绍了SpringBoot+Tess4j实现牛的OCR识别工具的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • 详解Java的文件与目录管理以及输入输出相关操作

    详解Java的文件与目录管理以及输入输出相关操作

    这篇文章主要介绍了详解Java的文件与目录管理以及输入输出相关操作,是Java入门学习中的基础知识,需要的朋友可以参考下
    2015-09-09
  • 通过ibatis解决sql注入问题

    通过ibatis解决sql注入问题

    这篇文章主要介绍了通过ibatis解决sql注入问题,需要的朋友可以参考下
    2017-09-09
  • 最新jsonwebtoken-jwt 0.12.3 基本使用小结

    最新jsonwebtoken-jwt 0.12.3 基本使用小结

    这篇文章主要介绍了最新jsonwebtoken-jwt 0.12.3 基本使用小结,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-12-12

最新评论