面试笔记——线程池

线程池的核心参数(原理)

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

  • corePoolSize 核心线程数目
  • maximumPoolSize 最大线程数目 = (核心线程+救急线程的最大数目)
  • keepAliveTime 生存时间 : 救急线程的生存时间,生存时间内没有新任务,此线程资源会释放
  • unit 时间单位 :救急线程的生存时间单位,如秒、毫秒等
  • workQueue :当没有空闲核心线程时,新来任务会加入到此队列排队,队列满会创建救急线程执行任务
  • threadFactory 线程工厂 :可以定制线程对象的创建,例如设置线程名字、是否是守护线程等
  • handler 拒绝策略:当所有线程都在繁忙,workQueue 也放满时,会触发拒绝策略

在这里插入图片描述
使用Demo:

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class ThreadPoolDemo implements Runnable {

    public static void main(String[] args) {

        //创建阻塞队列
        LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(100);

        ArrayBlockingQueue<Runnable> arrayBlockingQueue = new ArrayBlockingQueue<>(5);


        //创建工厂
        ThreadFactory threadFactory = new ThreadFactory() {

            AtomicInteger atomicInteger = new AtomicInteger(1);
            @Override
            public Thread newThread(Runnable r) {
                //创建线程把任务传递进去
                Thread thread = new Thread(r);
                //设置线程名称
                thread.setName("MyThread: "+atomicInteger.getAndIncrement());
                return thread;
            }
        };
        ThreadPoolExecutor pool  = new ThreadPoolExecutor(
                2,
                5,
                1,
                TimeUnit.SECONDS,
                arrayBlockingQueue,
                threadFactory,
                new ThreadPoolExecutor.DiscardOldestPolicy());
        for (int i = 0; i < 100; i++) {
            pool.submit(new ThreadPoolDemo());
        }

        pool.shutdown();
    }

    @Override
    public void run() {
        //执行业务
        System.out.println(Thread.currentThread().getName()+" 进来了");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"出去了");
    }
}

线程池中常见的阻塞队列

workQueue:当没有空闲核心线程时,新来任务会加入到此队列排队,队列满会创建救急线程执行任务。

  1. ArrayBlockingQueue:基于数组结构的有界阻塞队列,FIFO。
  2. LinkedBlockingQueue:基于链表结构的有界阻塞队列,FIFO。
  3. DelayedWorkQueue :是一个优先级队列,它可以保证每次出队的任务都是当前队列中执行时间最靠前的
  4. SynchronousQueue:不存储元素的阻塞队列,每个插入操作都必须等待一个移出操作。

ArrayBlockingQueue的LinkedBlockingQueue区别:在这里插入图片描述

确定核心线程数

  • IO密集型任务:核心线程数大小设置为2N+1(N为当前CPU的核数)
    • 一般来说:文件读写、DB读写、网络请求等
  • CPU密集型任务:核心线程数大小设置为N+1
    • 一般来说:计算型代码、Bitmap转换、Gson转换等

查看机器的CPU核数:

public static void main(String[] args) {
	//查看机器的CPU核数
	System.out.println(Runtime.getRuntime().availableProcessors());
}

参考回答:

  • 高并发、任务执行时间短 :( CPU核数+1 ),减少线程上下文的切换
  • 并发不高、任务执行时间长
    • IO密集型的任务 : (CPU核数 * 2 + 1)
    • 计算密集型任务 :( CPU核数+1 )
  • 并发高、业务执行时间长,解决这种类型任务的关键不在于线程池而在于整体架构的设计,看看这些业务里面某些数据是否能做缓存是第一步,增加服务器是第二步,至于线程池的设置,设置参考上一条

线程池的种类

在java.util.concurrent.Executors类中提供了大量创建连接池的静态方法,以下四种比较常见。
1. 创建使用固定线程数的线程池 ——适用于任务量已知,相对耗时的任务

public static ExecutorService newFixedThreadPool(int nThreads) {
   return new ThreadPoolExecutor(nThreads, nThreads,
                                 0L, TimeUnit.MILLISECONDS,
                                 new LinkedBlockingQueue<Runnable>());
}

核心线程数与最大线程数一样,没有救急线程
阻塞队列是LinkedBlockingQueue,最大容量为Integer.MAX_VALUE
举例:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class FixedThreadPoolCase {

    static class FixedThreadDemo implements Runnable{
        @Override
        public void run() {
            String name = Thread.currentThread().getName();
            for (int i = 0; i < 2; i++) {
                System.out.println(name + ":" + i);
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        //创建一个固定大小的线程池,核心线程数和最大线程数都是3
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        for (int i = 0; i < 5; i++) {
            executorService.submit(new FixedThreadDemo());
            Thread.sleep(10);
        }

        executorService.shutdown();
    }

}

运行结果:

pool-1-thread-1:0
pool-1-thread-1:1
pool-1-thread-2:0
pool-1-thread-2:1
pool-1-thread-3:0
pool-1-thread-3:1
pool-1-thread-1:0
pool-1-thread-1:1
pool-1-thread-2:0
pool-1-thread-2:1

2. 单线程化的线程池,它只会用唯一的工作线程来执行任 务,保证所有任务按照指定顺序(FIFO)执行——适用于按照顺序执行的任务

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

核心线程数和最大线程数都是1
阻塞队列是LinkedBlockingQueue,最大容量为Integer.MAX_VALUE

举例:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class NewSingleThreadCase {

    static int count = 0;

    static class Demo implements Runnable {
        @Override
        public void run() {
            count++;
            System.out.println(Thread.currentThread().getName() + ":" + count);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        //单个线程池,核心线程数和最大线程数都是1
        ExecutorService exec = Executors.newSingleThreadExecutor();

        for (int i = 0; i < 10; i++) {
            exec.execute(new Demo());
            Thread.sleep(5);
        }
        exec.shutdown();
    }

}

运行结果:

pool-1-thread-1:1
pool-1-thread-1:2
pool-1-thread-1:3
pool-1-thread-1:4
pool-1-thread-1:5
pool-1-thread-1:6
pool-1-thread-1:7
pool-1-thread-1:8
pool-1-thread-1:9
pool-1-thread-1:10

3. 可缓存线程池——适合任务数比较密集,但每个任务执行时间较短的情况

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

核心线程数为0
最大线程数是Integer.MAX_VALUE
阻塞队列为SynchronousQueue:不存储元素的阻塞队列,每个插入操作都必须等待一个移出操作。

举例:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CachedThreadPoolCase {

    static class Demo implements Runnable {
        @Override
        public void run() {
            String name = Thread.currentThread().getName();
            try {
                //修改睡眠时间,模拟线程执行需要花费的时间
                Thread.sleep(100);

                System.out.println(name + "执行完了");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        //创建一个缓存的线程,没有核心线程数,最大线程数为Integer.MAX_VALUE
        ExecutorService exec = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            exec.execute(new Demo());
            Thread.sleep(1);
        }
        exec.shutdown();
    }

}

运行结果:

pool-1-thread-1执行完了
pool-1-thread-2执行完了
pool-1-thread-3执行完了
pool-1-thread-4执行完了
pool-1-thread-5执行完了
pool-1-thread-6执行完了
pool-1-thread-7执行完了
pool-1-thread-8执行完了
pool-1-thread-9执行完了
pool-1-thread-10执行完了

4. 提供了“延迟”和“周期执行”功能的ThreadPoolExecutor。

public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue());
}
public ScheduledThreadPoolExecutor(int corePoolSize,
                                   ThreadFactory threadFactory) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue(), threadFactory);
}
public ScheduledThreadPoolExecutor(int corePoolSize,
                                   RejectedExecutionHandler handler) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue(), handler);
}
public ScheduledThreadPoolExecutor(int corePoolSize,
                                   ThreadFactory threadFactory,
                                   RejectedExecutionHandler handler) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue(), threadFactory, handler);
}

举例

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledThreadPoolCase {

    static class Task implements Runnable {
        @Override
        public void run() {
            try {
                String name = Thread.currentThread().getName();

                System.out.println(name + ", 开始:" + new Date());
                Thread.sleep(1000);
                System.out.println(name + ", 结束:" + new Date());

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        //按照周期执行的线程池,核心线程数为2,最大线程数为Integer.MAX_VALUE
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(2);
        System.out.println("程序开始:" + new Date());

        /**
         * schedule 提交任务到线程池中
         * 第一个参数:提交的任务
         * 第二个参数:任务执行的延迟时间
         * 第三个参数:时间单位
         */
        scheduledThreadPool.schedule(new Task(), 0, TimeUnit.SECONDS);
        scheduledThreadPool.schedule(new Task(), 1, TimeUnit.SECONDS);
        scheduledThreadPool.schedule(new Task(), 5, TimeUnit.SECONDS);

        Thread.sleep(5000);

        // 关闭线程池
        scheduledThreadPool.shutdown();

    }

}

运行结果:

程序开始:Mon Apr 29 22:26:18 CST 2024
pool-1-thread-1, 开始:Mon Apr 29 22:26:18 CST 2024
pool-1-thread-2, 开始:Mon Apr 29 22:26:19 CST 2024
pool-1-thread-1, 结束:Mon Apr 29 22:26:19 CST 2024
pool-1-thread-2, 结束:Mon Apr 29 22:26:20 CST 2024
pool-1-thread-1, 开始:Mon Apr 29 22:26:23 CST 2024
pool-1-thread-1, 结束:Mon Apr 29 22:26:24 CST 2024

综上:
newFixedThreadPool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任 务,保证所有任务按照指定顺序(FIFO)执行
newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程
newScheduledThreadPool:可以执行延迟任务的线程池,支持定时及周期性任务执行

为什么不使用Excutors创建线程池

参考阿里开发手册《Java开发手册-嵩山版》
在这里插入图片描述
ps:OOM是指内存溢出。
最后,建议根据计算机的条件使用ThreadPoolExecutor创建线程池(突然感觉上一节白学了,唉~)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/583579.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

25计算机考研院校数据分析 | 四川大学

四川大学(Sichuan University)简称“川大”&#xff0c;由中华人民共和国教育部直属&#xff0c;中央直管副部级建制&#xff0c;是世界一流大学建设高校、985工程”、"211工程"重点建设的高水平综合性全国重点大学&#xff0c;入选”2011计划"、"珠峰计划…

PostgreSQL的学习心得和知识总结(一百四十)|深入理解PostgreSQL数据库 psql工具 \set 变量内部及HOOK机制

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《PostgreSQL数据库内核分析》 2、参考书籍&#xff1a;《数据库事务处理的艺术&#xff1a;事务管理与并发控制》 3、PostgreSQL数据库仓库…

【能力展现】魔改ZXING源码实现商业级DM码检测能力

学习《OpenCV应用开发&#xff1a;入门、进阶与工程化实践》一书 做真正的OpenCV开发者&#xff0c;从入门到入职&#xff0c;一步到位&#xff01; 什么是DM码 dataMatrix是一种二维码&#xff0c;原名datacode&#xff0c;由美国国际资料公司于1989年发明。dataMatrix二维码…

GuildFi升级为Zentry的背后 链游公会的探索与转型

​链游即区块链游戏&#xff0c;指依托区块链技术构建的游戏产品。其与传统游戏的最大区别在于区块链的去中心化特性对玩家的资产有着天然的确权行为&#xff0c;因此玩家在链游中的资产是作为玩家的个人资产存在。较于 GameFi 来说&#xff0c;链游的包含范围更大&#xff0c;…

吴恩达机器学习笔记:第 8 周-14降维(Dimensionality Reduction) 14.3-14.5

目录 第 8 周 14、 降维(Dimensionality Reduction)14.3 主成分分析问题14.4 主成分分析算法14.5 选择主成分的数量 第 8 周 14、 降维(Dimensionality Reduction) 14.3 主成分分析问题 主成分分析(PCA)是最常见的降维算法。 在 PCA 中&#xff0c;我们要做的是找到一个方向…

【高校科研前沿】华东师大白开旭教授博士研究生李珂为一作在RSE发表团队最新成果:基于波谱特征优化的全球大气甲烷智能反演技术

文章简介 论文名称&#xff1a;Developing unbiased estimation of atmospheric methane via machine learning and multiobjective programming based on TROPOMI and GOSAT data&#xff08;基于TROPOMI和GOSAT数据&#xff0c;通过机器学习和多目标规划实现大气甲烷的无偏估…

OS复习笔记ch5-1

引言 讲解完进程和线程之后&#xff0c;我们就要来到进程的并发控制这里&#xff0c;这一章和下一章是考试喜欢考察的点&#xff0c;有可能会出大题&#xff0c;面试也有可能会被频繁问到&#xff0c;所以章节内容较多。请小伙伴们慢慢食用&#xff0c;看完之后多思考加强消化…

【JPE】顶刊测算-工业智能化数据(附stata代码)

数据来源&#xff1a;国家TJ局、CEC2008、IFR数据 时间跨度&#xff1a;2006-2019年 数据范围&#xff1a;各省、地级市 数据指标&#xff1a; 本数据集展示了2006-2019年各省、各地级市的共工业智能化水平的数据。本数据集包含三种构建工业机器人密度来反映工业智能化水平的方…

基于Springboot的数字化农家乐管理平台(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的数字化农家乐管理平台&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系…

Apache Seata基于改良版雪花算法的分布式UUID生成器分析2

title: 关于新版雪花算法的答疑 author: selfishlover keywords: [Seata, snowflake, UUID, page split] date: 2021/06/21 本文来自 Apache Seata官方文档&#xff0c;欢迎访问官网&#xff0c;查看更多深度文章。 关于新版雪花算法的答疑 在上一篇关于新版雪花算法的解析中…

web前端学习笔记4

4. 盒子模型 4.0 代码地址 https://gitee.com/qiangge95243611/java118/tree/master/web/day044.1 什么是盒子模型(Box Model) 所有HTML元素可以看作盒子,在CSS中,"box model"这一术语是用来设计和布局时使用。 CSS盒模型本质上是一个盒子,封装周围的HTML元素,…

在Docker中部署Java应用:Java版本隔离的实践案例

在Docker中部署Java应用&#xff1a;Java版本隔离的实践案例 人生就是一场又一场的相遇&#xff0c;一个明媚&#xff0c;一个忧伤&#xff0c;一个华丽&#xff0c;一个冒险&#xff0c;一个倔强&#xff0c;一个柔软&#xff0c;最后那个正在成长。 背景需求 在软件开发和部…

Debian 12 -bash: netstat: command not found 解决办法

问题表现&#xff1a; debian 12系统中&#xff0c;不能使用 netstat命令 处理办法&#xff1a; netstat 命令就的net-tools中&#xff0c;把net-tools工具安装上就好了。 apt-get install netstat 安装之后就可以使用netstat 命令了&#xff0c;如查询端口情况&#xff1a; …

基于SpringBoot+Vue高校宣讲会管理系统设计与实现

项目介绍&#xff1a; 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装高校宣讲会管理系统软件来发挥其高效地信息…

C# Web控件与数据感应之 Control 类

目录 关于数据感应 Control 类 范例运行环境 simpleDataListEx方法 设计 实现 调用示例 数据源 调用 小结 关于数据感应 数据感应也即数据捆绑&#xff0c;是一种动态的&#xff0c;Web控件与数据源之间的交互&#xff0c;诸如 ListControl 类类型控件&#xff0c;在…

pytest教程-35-钩子函数-pytest_unconfigure

领取资料&#xff0c;咨询答疑&#xff0c;请➕wei: June__Go 上一小节我们学习了pytest_configure钩子函数的使用方法&#xff0c;本小节我们讲解一下pytest_unconfigure钩子函数的使用方法。 pytest_unconfigure(config) 是一个 pytest 钩子函数&#xff0c;它在 pytest 退…

【linux运维】vim基础应用

系列综述&#xff1a; &#x1f49e;目的&#xff1a;本系列是个人整理为了学习基本的shell编程和linux命令&#xff0c;整理期间苛求每个知识点&#xff0c;平衡理解简易度与深入程度。 &#x1f970;来源&#xff1a;材料主要源于b站大学——linux运维课程进行的&#xff0c;…

【MHA】MySQL高可用MHA源码1-主库故障监控

1 阅读之前的准备工作 1 一个IDE工具 &#xff0c;博主自己尝试了vscode安装perl的插件&#xff0c;但是函数 、变量 、模块等都不能跳转&#xff0c;阅读起来不是很方便。后来尝试使用了pycharm安装perl插件&#xff0c;阅读支持跳转&#xff0c;自己也能写一些简单的测试样例…

[iOS]组件化开发

一、组件化开发基础 1.组件定义 在软件开发中&#xff0c;一个组件是指一个独立的、可替换的软件单元&#xff0c;它封装了一组相关的功能。组件通过定义的接口与外界交互&#xff0c;并且这些接口隔离了组件内部的实现细节。在Swift语言中&#xff0c;组件可以是一个模块、一…

CCF-CSP真题题解:201312-2 ISBN号码

201312-2 ISBN号码 #include <iostream> #include <cstring> #include <algorithm> using namespace std;string s;int main() {cin >> s;int num 0;for (int i 0, p 1; i < s.size() - 1; i)if (s[i] ! -) {num (s[i] - 0) * p;p;}num % 11;ch…
最新文章