狂野java前置课程-线程池的基本使用

news2024/11/26 16:40:58

回顾

什么是线程,什么是进程?

  • 进程:是一个应用程序,里面包含很多线程
  • 线程:进程执行的基本单元

java实现线程的几种方式

  • 继承Thread类
  • 实现Runable接口

线程的生命周期

在这里插入图片描述

执行线程会出现的问题

  • 一个线程只能执行一个任务
  • 线程执行完后销毁,不能复用
  • 线程过多,会导致jvm宕机(正常一台8核【同时并行的线程最多只有8个,cpu会在2000个线程中来回切】,12g的服务器线程数正常只有2000个线程)

线程池

JUC

  • JUC是一个工具类,用户高并发、处理多线程的一个包。
    在这里插入图片描述

线程池解决了哪些问题

  • 降低资源消耗
  • 方便线程数的管控
  • 功能强大,提供延时定时线程池

线程池引发了什么问题

  • jvm宕机,提交的任务会消失
  • 使用不合理,导致内存溢出
  • 参数多,引入数据结构和算法,增加了学习难度

线程池的设计思想

  • 线程维护
    在这里插入图片描述

  • 执行任务
    在这里插入图片描述

  • 状态监控
    在这里插入图片描述

线程池的原理

  • 线程池的结构图
    在这里插入图片描述
  • 最常用的是ThreadPoolExecutor,调度用ScheduleExecutorService;

线程池的工作状态

在这里插入图片描述

  • RUNNING:线程池一单被创建,就是RUNNING状态。
  • SHUTDOWN:不接受新的任务,但能处理已添加的任务。调用shutdown接口后,线程池的状态由RUNNING变成SHUTDOWN。
    在这里插入图片描述
  • STOP:不接受新的任务,不会去处理已经添加的任务,并且会中断住在处理的任务;【调用shutdownnow()接口之后线程池会由running和shutdown变成stop】
  • TIDYING(终止状态):所有任务终止,队列中任务数量会变成0。会执行钩子函数terminated()方法。

线程池的参数定义

在这里插入图片描述

线程池的结构说明

在这里插入图片描述

  • 任务提交给线程池,如果有空闲线程,则会将任务分配给空闲线程,如果没有空闲线程会先放到队列中去。
  • 如果核心线程都满了,并且队列也满了,才会去只用最大线程数中的线程。
  • 达到maxSize,根据拒绝策略处理。

线程池的工具类

在这里插入图片描述

确定线程池的线程数

创建合适的线程数才能提高性能

  • io密集型任务
    io操作时间长,cpu利用率不高,这类任务CPU常处于空闲状态。

此类型任务可以开cpu核心的两倍线程。比如cpu是4核的,可以开8个线程。

  • cpu密集型任务
    执行计算任务,cpu一直运行,cpu的利用率高 。

取相等的线程数。比如cpu是4核的,可以开4个线程。

  • 混合型任务
    既要执行逻辑运算,又要进行大量的io操作。针对不同类型的任务,创建不同的线程池。

最佳线程数 = ((线程等待时间+线程cpu时间)/ 线程cpu时间)* cpu核数
在这里插入图片描述

线程池的源码分析

  • ExecutorService executorService = Executors.newFixedThreadPool(5);
    在这里插入图片描述
  • ExecutorService executorService1 = Executors.newSingleThreadExecutor();
    在这里插入图片描述
  • ExecutorService executorService2 = Executors.newCachedThreadPool();
    任务先放在阻塞队列中在进行处理
    在这里插入图片描述
  • ExecutorService executorService3 = Executors.newScheduledThreadPool(5);
    在这里插入图片描述

为什么大厂里禁止使用Executors工具类,怕使用不规范造成宕机的风险。一般都是自己new一个线程池:ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20, 0L, TimeUnit.HOURS, new LinkedBlockingQueue<>(1000));

threadPoolExecutor.execute(); //提交一个普通的任务
threadPoolExecutor.submit(); //提交一个有返回值的任务

  • execute()
//提交任务代码
 public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        //判断工作数,如果小于coreSize -> addWork,注意第二个参数 core=true
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        //否则,如果线程池还在运行,offer到队列
        if (isRunning(c) && workQueue.offer(command)) {
        	//在检查一下状态
            int recheck = ctl.get();
            //线程池终止,直接移除任务
            if (! isRunning(recheck) && remove(command))
                reject(command);
            //如果没可用线程的话(coreSize=0),创建一个空work
            //该work创建时指派给任务(为null)。会被放入works集合,从队列获取任务去执行
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        //队列已满,继续调addWork,但是注意,core=false,开启到maxSize的大门
        //超出max,addWork会返回false,进入reject
        else if (!addWorker(command, false))
            reject(command);
    }
  • addWorker()
 private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                //线程大于 2^29次方^-1,或者线程超出了规定的范围,返回false
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();
                if (runStateOf(c) != rs)
                    continue retry;
            }
        }

		//创建work放入works集合(一个hashSet)
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
        	//符合条件,创建新的work包装成task
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                //加锁,works是一个hashset,保证线程安全性
                mainLock.lock();
                try {
                    int rs = runStateOf(ctl.get());

                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                	//add成功新的work,work立即启动
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }
  • 任务获取和执行
//执行runWorker()的时候,一直循环,如果携带task,就执行
while (task != null || (task = getTask()) != null)

private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?

        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }

            int wc = workerCountOf(c);

            // Are workers subject to culling?
            //判断是不是超时处理,决定了当前线程是不是要释放
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
			//线程数量超出max,并且上次循环poll等待超时,说明该线程已经终止,将线程数量原子性 减
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                //计数器原子性递减,递减成功后,放回null,for终止
                if (compareAndDecrementWorkerCount(c))
                    return null;
                //递减失败,继续下一轮循环
                continue;
            }

            try {
                //如果线程可被释放,那就poll,释放时间为keepAliveTime
                //否则,线程不会被释放,take一直被阻塞在这里,直到新的任务继续工作
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                //到这说明可被释放的线程等待超时,已被销毁,设置标记,下次循环的线程减少
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

线程池的经典面试题

  1. 线程池如何保证线程不被销毁

如果队列没有任务,核心线程一直阻塞获取任务的方法,直到返回任务。而任务执行完后,又会进入下一轮work.runWork()中循环。

//核心代码
//work.runWork
while (task != null || (task = getTask()) != null)

//work.getTask
 boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
 //最关键
 Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS):workQueue.take();
  1. 核心线程和非核心线程有区别吗?

没有,被销毁的线程和创建先后无关。即使是第一个被创建的核心线程,仍有可能被销毁。
**验证:**每个work在runwork()的时候去getTask(),在getTask内部,并没有针对性的区分当前work是否是核心线程。只要判断work是都大于core,就会调poll(),否则take()。

  1. 阅读代码,查看执行结果
    在这里插入图片描述

结果只会执行 1 和 2,因为队列不会满,只会执行核心线程数,而核心线程在 while(true) 中一直在执行。

  1. 线程池7个参数作用和生效时机
  • int corePoolSize,
  • int maximumPoolSize,
  • long keepAliveTime,
  • TimeUnit unit,
  • BlockingQueue workQueue,
  • ThreadFactory threadFactory
  • RejectedExecutionHandler handler
  1. 为什么线程池不用数组,而用队列?

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【专为苛刻的数据环境而构建】上海道宁为您带来世界上先进的矢量原生、时间序列和实时分析数据库——kdb系列产品

kdb是高效的矢量原生 时间序列和实时分析数据库 专为高性能矢量 数据驱动的应用程序而构建 以加速云端、数据仓库和 数据湖中的 AI 和 ML 工具 从而更快、更高效地 制定业务决策 使用数据时间库加速数据 分析和生成 AI 管道 以降低成本 提高性能并提高效率 开发商介绍…

移动端图形API通讲(一)--从Gles、Vulkan到Metal

转载请注明&#xff0c;来自leonnwei的csdn blog 引言 一直想整理下关于移动端图形编程API的文档。图形API为何重要&#xff1f;如果说图形编程的内功是计算机图形学的诸原理和算法&#xff0c;那么外功就是实实在在的硬件API。不能精通API的使用&#xff0c;就无法把渲染特性合…

新来个技术总监,把限流实现的那叫一个优雅,佩服!

在电商高并发场景下&#xff0c;我们经常会使用一些常用方法&#xff0c;去应对流量高峰&#xff0c;比如限流、熔断、降级&#xff0c;今天我们聊聊限流。 什么是限流呢&#xff1f;限流是限制到达系统的并发请求数量&#xff0c;保证系统能够正常响应部分用户请求&#xff0…

人脸比对指标 -- 人脸相似度

目前市面上既有OpenCV等开源算法库&#xff0c;很多芯片厂商的产品也自带简单算法&#xff0c;同时专业算法大厂也会开放相关技术&#xff0c;如提供免费、离线人脸识别SDK的虹软视觉开放平台等。对于开发者而言&#xff0c;面对多种算法&#xff0c;如何进判断算法性能至关重要…

Vue初始化项目加载逻辑

Vue初始化项目加载逻辑 项目创建 我们只需要创建项目即可&#xff0c;剩余的依赖都没必要安装 我们先来看main.js,咱们加了一行备注 import Vue from vue import App from ./App.vue import router from ./router import store from ./storeVue.config.productionTip fals…

汇众智,奔涌向前赢未来 | 2023开放原子全球开源峰会 OpenAtom openEuler 分论坛即将启幕

OpenAtom openEuler&#xff08;以下简称“openEuler”&#xff09;自 2021 年贡献给开放原子开源基金会后&#xff0c;步入高速发展阶段&#xff0c;社区每日活跃开发者近 4000 人&#xff0c;新增讨论 2168 次&#xff0c;代码合入 127 个、软件包 31 个&#xff1b;每月新增…

【python】之cowsay库,打印图案!

以前经常看到互联网大佬的Linux shell脚本启动的时候显示一副炫酷的字符画面&#xff0c;逼格瞬间提升一个档次&#xff0c;按说字符拼画也不是什么难事&#xff0c;只要有时间、有耐心(然鹅大部分人这两样都没有)。 网上流传着一个很老的库cawsay牛说。cowsay是一个生成ASCII…

MMDection学习记录(一)之环境配置

Linux下环境配置 创建环境并激活 conda create --name openmmlab python3.7 -y conda activate openmmlab安装Pytorch 建议使用pip命令安装&#xff0c;否则会报错&#xff1a; symbol free_gemm_select version libcublasLt.so.11 not defined in file libcublasLt.so.11 w…

24节气-芒种 || 一分耕耘,一分收获。

“时雨及芒种&#xff0c;四野皆插秧。” 芒种&#xff0c;农历二十四节气中的第 9 个节气&#xff0c;夏季的第 3 个节气&#xff0c;表示仲夏时节的正式开始。 芒种意味着谷物播种的好时机&#xff0c;农事耕种的劳动量会大大增加&#xff0c;因此芒种也有“忙种”的说法。…

TrueBot活动的惊人激增揭示了新的交付载体

网络安全研究人员披露&#xff0c;在2023年5月观察到TrueBot活动激增的情况。 "VMware的Fae Carlisle说&#xff1a;"TrueBot是一个下载器木马僵尸网络&#xff0c;它使用命令和控制服务器来收集被攻击系统的信息&#xff0c;并将被攻击系统作为进一步攻击的发起点。…

云原生网关Apache APISIX

Apache APISIX 介绍 什么是Apache APISIX Apache APISIX 是一个动态、实时、高性能的云原生 API 网关&#xff0c;提供了负载均 衡、动态上游、灰度发布、服务熔断、身份认证、可观测性等丰富的流量管理功 能。可以使用 Apache APISIX 处理传统的南北向流量&#xff0c;也可以…

数据可视化 - 派可数据商业智能BI可视化分析平台

有一点可能很多人没有想到&#xff0c;实际上商业智能BI的相关概念已经有了数十年的发展历史。在这段发展过程中&#xff0c;商业智能BI形成了一套成熟的理论和产品体系&#xff0c;并且在现代的信息化、数字化加成下&#xff0c;成为了各行各业企业的成熟产品。 一、商业智能…

ROS学习笔记(十):机器人建模

ROS学习笔记&#xff08;十&#xff09;&#xff1a;机器人建模 统一机器人描述格式&#xff08;URDF&#xff09;link标签joint标签robot标签gazebo标签 URDF检查URDF可视化 统一机器人描述格式&#xff08;URDF&#xff09; URDF&#xff08;Unified Robot Description Form…

springboot项目 + rancher管理 实现用户无感知部署

springboot项目 rancher管理 实现用户无感知部署 rancher/yaml设置 在rancher找到对应服务的config&#xff0c;将Minimum Ready设置为合适的时间&#xff08;单位为秒&#xff09; 或者以yaml文件修改配置&#xff0c;但是在yaml配置中是叫minReadySeconds spec: # 定义de…

95后自述,00后都这么卷了吗?

在程序员职场上&#xff0c;什么样的人最让人反感呢? 是技术不好的人吗?并不是。技术不好的同事&#xff0c;我们可以帮他。 是技术太强的人吗?也不是。技术很强的同事&#xff0c;可遇不可求&#xff0c;向他学习还来不及呢。 真正让人反感的&#xff0c;是技术平平&#x…

【云原生 · Docker】轻松学会dockerfile构建镜像

目录 &#x1f349;dockerfile是什么 &#x1f349;镜像的缓存特性 &#x1f349;dockerfile命令 &#x1f352;FROM &#x1f352;RUN &#x1f352;CMD &#x1f352;LABEL &#x1f352;EXPOSE &#x1f352;ENV &#x1f352;ADD &#x1f352;COPY &#x1f352;ENTRYPOIN…

浅析国有企业信息化建设的难点及解决对策

01 国有企业信息化部门主要做什么&#xff1f; 国有企业信息化部门负责在企业范围内推进信息化工作。他们的职责包括但不限于&#xff1a;制定信息化战略和规划、实施信息化项目、管理信息系统和网络、维护和升级软硬件、开发新的数字化产品和服务、提高信息安全和数据隐私等。…

【正点原子STM32连载】 第二十七章 RTC实时时钟实验摘自【正点原子】STM32F103 战舰开发指南V1.2

1&#xff09;实验平台&#xff1a;正点原子stm32f103战舰开发板V4 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id609294757420 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/thread-340252-1-1.html 第二十…

spring 注解 @RequestPart @RequestParam 获取文件流 MultipartFile ,读取Excel等文件

RequestPart RequestPart这个注解用在 multipart/form-data 表单提交请求的方法上。 RequestParam 也可以携带文件 RequestParam也同样支持 multipart/form-data 请求。 RequestParam和RequestPart的区别 RequestParam 适用于 name-valueString 类型的请求域&#xff0c;R…

毕业五六年,明明技术越来越好,而我混的却越来越惨了...

别人都是越来越好&#xff0c;而我是越来越差&#xff01; 至于为什么会这样&#xff0c;可能是因为自己年轻气盛&#xff1b;也可能学历不高&#xff0c;仅仅是个本科&#xff1b;也可能是因为整体环境不好&#xff1b;也可能是能力不足&#xff1b;也可能是运气不好......也可…