线程池底层实现原理相关面试题-(面试必问)

本文探讨了线程池的概念、使用原因、应用场景和核心作用,并详细讲解了线程池的创建方式,尤其是底层复用机制。强调了线程池在限制资源消耗、提高响应速度和管理线程方面的重要性。还分析了Java中ExecutorService的参数配置、自定义线程池以及线程池任务处理的拒绝策略。最后,文章提到了阿里巴巴不建议直接使用Executors的原因,因为可能导致线程池溢出。
摘要由CSDN通过智能技术生成

线程池底层实现原理相关面试题-(面试必问)

大家好,我是酷酷的韩~
酷酷的韩金群

一.谈谈什么是线程池

线程池和数据库连接池非常类似,可以统一管理和维护线程,减少没有必要的开销。

二.为什么要使用线程池

一个线程的周期会经过以下几个阶段:
在这里插入图片描述

因为频繁的开启线程或者停止线程,线程需要从新被 cpu 从就绪到运行状态调度,需要发生
cpu 的上下文切换(每次都要耗费服务器资源与时间),效率非常低。

三.在哪些地方会使用到线程池?

在实际的开发项目中禁止自己new线程
发短信、发邮件。量大的用mq

四.线程池有哪些作用?

核心点:复用机制 提前创建好固定的线程一直在运行状态 实现复用 限制线程创建数量。
1.降低资源消耗:通过池化技术重复利用已创建的线程,降低线程创建和销毁造成的损耗。

2.提高响应速度:任务到达时,无需等待线程创建即可立即执行。

3.提高线程的可管理性:线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会因为线程的不合理分布导致资源调度失衡,降低系统的稳定性。使用线程池可以进行统一的分配、调优和监控。

4.提供更多更强大的功能:线程池具备可拓展性,允许开发人员向其中增加更多的功能。比如延时定时线程池 ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行。

五.线程池的创建方式?

可通过jdk自带的方式创建,比如Executors,有以下几种:
Executors.newCachedThreadPool(); 可缓存线程池
Executors.newFixedThreadPool();可定长度 限制最大线程数
Executors.newScheduledThreadPool() ; 可定时
Executors.newSingleThreadExecutor(); 单例

六线程池底层复用机制的原理?(重点)

本质思想:创建一个线程,不会立马停止或者销毁,而是一直存在实现复用。

1.提前创建固定大小的线程一直保持在正在运行状态(可能会非常消耗cpu资源)

2.当需要线程执行任务,将该任务提交缓存在并发队列中;如果缓存队列满了,则会执行拒绝策略。

3.正在运行的线程从并发队列中获取任务从而实现多线程复用问题。

4.线程池队列实现原理图:
在这里插入图片描述

七.Java纯手写线程池

package com.han.hjqdemo1.entity.d0228;

import java.util.ArrayList; import java.util.List; import java.util.concurrent.BlockingDeque; import java.util.concurrent.LinkedBlockingDeque;

/**  * @Author: hanjinqun  * @DateTime: 2022/2/28  * @Description: 手写线程池  */ public class Test2 {
    private List<WorkThread> workThreads;
    private BlockingDeque<Runnable> runnableBlockingDeque;//并发队列
    private boolean flag = true;

    /**
     * maxThreadCount 最大线程数
     * dequeSize 最大并发队列数
     */
    public Test2(int maxThreadCount, int dequeSize) {
        runnableBlockingDeque = new LinkedBlockingDeque<Runnable>(dequeSize);
        workThreads = new ArrayList<WorkThread>(maxThreadCount);
        for (int i = 0; i < maxThreadCount; i++) {
            new WorkThread().start();
        }
    }

    class WorkThread extends Thread {
        @Override
        public void run() {
            while (flag || runnableBlockingDeque.size() > 0) {
                Runnable runnable = runnableBlockingDeque.poll();
                if (runnable != null) {
                    runnable.run();
                }

            }

        }
    }

    public boolean execute(Runnable runnable) {
        return runnableBlockingDeque.offer(runnable);
    }


    public static void main(String[] args) {
        Test2 test2 = new Test2(2, 2);
        for (int i = 0; i < 10; i++) {
            final int finalI = i;
            test2.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "," +
                            finalI);
                }
            });
        }
        test2.flag = false;

    } }

返回值有可能是2次,有可能是4次。4次的原因是两个创建的线程和两个缓存在线程池的线程同时触发。

八.ThreadPoolExecutors核心参数有哪些?

corePoolSize:核心线程数量 一直正在保持运行的线程
maximumPoolSize:最大线程数,线程池允许创建的最大线程数。
keepAliveTime:超出 corePoolSize 后创建的线程的存活时间。
unit:keepAliveTime 的时间单位。
workQueue:任务队列,用于保存待执行的任务。
threadFactory:线程池内部创建线程所用的工厂。
handler:任务无法执行时的处理器

九.ThreadPoolExecutors底层实现原理?

1.当线程数小于核心线程数时,创建线程。
2.当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
3.当线程数大于等于核心线程数,且任务队列已满 。
3.1 若线程数小于最大线程数,创建线程 。
3.2 若线程数等于最大线程数,抛出异常,拒绝任务 。
4.代码示例:

package com.han.hjqdemo1.entity.d0228;

import java.util.concurrent.*;

/**
 * @Author: hanjinqun
 * @DateTime: 2022/3/1
 * @Description: 线程池底层实现原理
 */
public class Test4 {

    public static void main(String[] args) {
        ExecutorService executorService = MyThreadPooolExecitor.newFixedThreadPool(2, 4, 5);
        for (int i = 1; i <= 10; i++) {
            final int finali = i;
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "," + finali);
                }
            });
        }
    }

}

MyThreadPooolExecitor类

package com.han.hjqdemo1.entity.d0228;

import java.util.concurrent.*;

/**
 * @Author: hanjinqun
 * @DateTime: 2022/3/1
 * @Description: TODO
 */
public class MyThreadPooolExecitor {
    public static ExecutorService newFixedThreadPool(int corePoolSize, int maxNumPoolSize, int blockingQueue) {
        return new ThreadPoolExecutor(corePoolSize, maxNumPoolSize, 60L, TimeUnit.MILLISECONDS,
                new LinkedBlockingDeque<>(blockingQueue), (RejectedExecutionHandler) new MyThreadPooolExecitor());
    }

}

MyExecutionHandler类(自定义处理hander)
package com.han.hjqdemo1.entity.d0228;

import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @Author: hanjinqun
 * @DateTime: 2022/3/2
 * @Description: TODO
 */
public class MyExecutionHandler implements ThreadFactory, RejectedExecutionHandler {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
//        System.out.print("拒绝");
//        r.run();

    }

    @Override
    public Thread newThread(Runnable r) {
        return null;
    }
}

十.线程次队列满了,任务会丢失吗?

可以自定义异拒绝异常,将该任务缓存到 redis、本地文件、mysql 中后期项目启动实现补偿。
1.AbortPolicy 丢弃任务,抛运行时异常
2.CallerRunsPolicy 执行任务
3.DiscardPolicy 忽视,什么都不会发生
4.DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务
5.实现 RejectedExecutionHandler 接口,可自定义处理器。

十一.为什么阿里巴巴不建议使用Executors?

因为Executors底层都是基于 ThreadPoolExecutor 构造函数封装的,ThreadPoolExecutor构造函数中是无界的队列缓存,有可能导致线程池溢出,最大线程数失效。
在这里插入图片描述
在这里插入图片描述
再牛逼的梦想,也抵不住傻逼般的坚持。 ------酷酷的韩~

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

韩金群

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值