Java之线程池的详细解析

news2024/11/26 7:34:46

1. 线程池

1.1 线程状态介绍

当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。线程对象在不同的时期有不同的状态。那么Java中的线程存在哪几种状态呢?Java中的线程

状态被定义在了java.lang.Thread.State枚举类中,State枚举类的源码如下:

public class Thread {
    
    public enum State {
    
        /* 新建 */
        NEW , 
​
        /* 可运行状态 */
        RUNNABLE , 
​
        /* 阻塞状态 */
        BLOCKED , 
​
        /* 无限等待状态 */
        WAITING , 
​
        /* 计时等待 */
        TIMED_WAITING , 
​
        /* 终止 */
        TERMINATED;
    
    }
    
    // 获取当前线程的状态
    public State getState() {
        return jdk.internal.misc.VM.toThreadState(threadStatus);
    }
    
}

通过源码我们可以看到Java中的线程存在6种状态,每种线程状态的含义如下

线程状态具体含义
NEW一个尚未启动的线程的状态。也称之为初始状态、开始状态。线程刚被创建,但是并未启动。还没调用start方法。MyThread t = new MyThread()只有线程象,没有线程特征。
RUNNABLE当我们调用线程对象的start方法,那么此时线程对象进入了RUNNABLE状态。那么此时才是真正的在JVM进程中创建了一个线程,线程一经启动并不是立即得到执行,线程的运行与否要听令与CPU的调度,那么我们把这个中间状态称之为可执行状态(RUNNABLE)也就是说它具备执行的资格,但是并没有真正的执行起来而是在等待CPU的度。
BLOCKED当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。
WAITING一个正在等待的线程的状态。也称之为等待状态。造成线程等待的原因有两种,分别是调用Object.wait()、join()方法。处于等待状态的线程,正在等待其他线程去执行一个特定的操作。例如:因为wait()而等待的线程正在等待另一个线程去调用notify()或notifyAll();一个因为join()而等待的线程正在等待另一个线程结束。
TIMED_WAITING一个在限定时间内等待的线程的状态。也称之为限时等待状态。造成线程限时等待状态的原因有三种,分别是:Thread.sleep(long),Object.wait(long)、join(long)。
TERMINATED一个完全运行完成的线程的状态。也称之为终止状态、结束状态

各个状态的转换,如下图所示:

1.2 线程池-基本原理

概述 :

提到池,大家应该能想到的就是水池。水池就是一个容器,在该容器中存储了很多的水。那么什么是线程池呢?线程池也是可以看做成一个池子,在该池子中存储很多个线程。

线程池存在的意义:

系统创建一个线程的成本是比较高的,因为它涉及到与操作系统交互,当程序中需要创建大量生存期很短暂的线程时,频繁的创建和销毁线程对系统的资源消耗有可能大于业务处理是对系

统资源的消耗,这样就有点"舍本逐末"了。针对这一种情况,为了提高性能,我们就可以采用线程池。线程池在启动的时,会创建大量空闲线程,当我们向线程池提交任务的时,线程池就

会启动一个线程来执行该任务。等待任务执行完毕以后,线程并不会死亡,而是再次返回到线程池中称为空闲状态。等待下一次任务的执行。

线程池的设计思路 :

  1. 准备一个任务容器

  2. 一次性启动多个(2个)消费者线程

  3. 刚开始任务容器是空的,所以线程都在wait

  4. 直到一个外部线程向这个任务容器中扔了一个"任务",就会有一个消费者线程被唤醒

  5. 这个消费者线程取出"任务",并且执行这个任务,执行完毕后,继续等待下一次任务的到来

1.3 线程池-Executors默认线程池

概述 : JDK对线程池也进行了相关的实现,在真实企业开发中我们也很少去自定义线程池,而是使用JDK中自带的线程池。

我们可以使用Executors中所提供的静态方法来创建线程池

static ExecutorService newCachedThreadPool() 创建一个默认的线程池 ​ static newFixedThreadPool(int nThreads) 创建一个指定最多线程数量的线程池

代码实现 :

package com.itheima.mythreadpool;
​
​
//static ExecutorService newCachedThreadPool()   创建一个默认的线程池
//static newFixedThreadPool(int nThreads)       创建一个指定最多线程数量的线程池
​
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
​
public class MyThreadPoolDemo {
    public static void main(String[] args) throws InterruptedException {
​
        //1,创建一个默认的线程池对象.池子中默认是空的.默认最多可以容纳int类型的最大值.
        ExecutorService executorService = Executors.newCachedThreadPool();
        //Executors --- 可以帮助我们创建线程池对象
        //ExecutorService --- 可以帮助我们控制线程池
​
        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName() + "在执行了");
        });
​
        //Thread.sleep(2000);
​
        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName() + "在执行了");
        });
​
        executorService.shutdown();
    }
}
​

1.4 线程池-Executors创建指定上限的线程池

使用Executors中所提供的静态方法来创建线程池

static ExecutorService newFixedThreadPool(int nThreads) : 创建一个指定最多线程数量的线程池

代码实现 :

package com.itheima.mythreadpool;
​
//static ExecutorService newFixedThreadPool(int nThreads)
//创建一个指定最多线程数量的线程池
​
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
​
public class MyThreadPoolDemo2 {
    public static void main(String[] args) {
        //参数不是初始值而是最大值
        ExecutorService executorService = Executors.newFixedThreadPool(10);
​
        ThreadPoolExecutor pool = (ThreadPoolExecutor) executorService;
        System.out.println(pool.getPoolSize());//0
​
        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName() + "在执行了");
        });
​
        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName() + "在执行了");
        });
​
        System.out.println(pool.getPoolSize());//2
//        executorService.shutdown();
    }
}
​

1.5 线程池-ThreadPoolExecutor

创建线程池对象 :

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(核心线程数量,最大线程数量,空闲线程最大存活时间,任务队列,创建线程工厂,任务的拒绝策略);

代码实现 :

package com.itheima.mythreadpool;
​
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
​
public class MyThreadPoolDemo3 {
//    参数一:核心线程数量
//    参数二:最大线程数
//    参数三:空闲线程最大存活时间
//    参数四:时间单位
//    参数五:任务队列
//    参数六:创建线程工厂
//    参数七:任务的拒绝策略
    public static void main(String[] args) {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(2,5,2,TimeUnit.SECONDS,new ArrayBlockingQueue<>(10), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());
​
        pool.shutdown();
    }
}

1.6 线程池-参数详解

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
    
corePoolSize:   核心线程的最大值,不能小于0
maximumPoolSize:最大线程数,不能小于等于0,maximumPoolSize >= corePoolSize
keepAliveTime:  空闲线程最大存活时间,不能小于0
unit:           时间单位
workQueue:      任务队列,不能为null
threadFactory:  创建线程工厂,不能为null      
handler:        任务的拒绝策略,不能为null  

1.7 线程池-非默认任务拒绝策略

RejectedExecutionHandler是jdk提供的一个任务拒绝策略接口,它下面存在4个子类。

ThreadPoolExecutor.AbortPolicy:             丢弃任务并抛出RejectedExecutionException异常。是默认的策略。
ThreadPoolExecutor.DiscardPolicy:          丢弃任务,但是不抛出异常 这是不推荐的做法。
ThreadPoolExecutor.DiscardOldestPolicy:    抛弃队列中等待最久的任务 然后把当前任务加入队列中。
ThreadPoolExecutor.CallerRunsPolicy:        调用任务的run()方法绕过线程池直接执行。

注:明确线程池对多可执行的任务数 = 队列容量 + 最大线程数

案例演示1:演示ThreadPoolExecutor.AbortPolicy任务处理策略

public class ThreadPoolExecutorDemo01 {
​
    public static void main(String[] args) {
​
        /**
         * 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s
         */
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS ,
                new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.AbortPolicy()) ;
​
        // 提交5个任务,而该线程池最多可以处理4个任务,当我们使用AbortPolicy这个任务处理策略的时候,就会抛出异常
        for(int x = 0 ; x < 5 ; x++) {
            threadPoolExecutor.submit(() -> {
                System.out.println(Thread.currentThread().getName() + "---->> 执行了任务");
            });
        }
    }
}

控制台输出结果

pool-1-thread-1---->> 执行了任务
pool-1-thread-3---->> 执行了任务
pool-1-thread-2---->> 执行了任务
pool-1-thread-3---->> 执行了任务

控制台报错,仅仅执行了4个任务,有一个任务被丢弃了

案例演示2:演示ThreadPoolExecutor.DiscardPolicy任务处理策略

public class ThreadPoolExecutorDemo02 {
    public static void main(String[] args) {
        /**
         * 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s
         */
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS ,
                new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.DiscardPolicy()) ;
​
        // 提交5个任务,而该线程池最多可以处理4个任务,当我们使用DiscardPolicy这个任务处理策略的时候,控制台不会报错
        for(int x = 0 ; x < 5 ; x++) {
            threadPoolExecutor.submit(() -> {
                System.out.println(Thread.currentThread().getName() + "---->> 执行了任务");
            });
        }
    }
}

控制台输出结果

pool-1-thread-1---->> 执行了任务
pool-1-thread-1---->> 执行了任务
pool-1-thread-3---->> 执行了任务
pool-1-thread-2---->> 执行了任务

控制台没有报错,仅仅执行了4个任务,有一个任务被丢弃了

案例演示3:演示ThreadPoolExecutor.DiscardOldestPolicy任务处理策略

public class ThreadPoolExecutorDemo02 {
    public static void main(String[] args) {
        /**
         * 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s
         */
        ThreadPoolExecutor threadPoolExecutor;
        threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS ,
                new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.DiscardOldestPolicy());
        // 提交5个任务
        for(int x = 0 ; x < 5 ; x++) {
            // 定义一个变量,来指定指定当前执行的任务;这个变量需要被final修饰
            final int y = x ;
            threadPoolExecutor.submit(() -> {
                System.out.println(Thread.currentThread().getName() + "---->> 执行了任务" + y);
            });     
        }
    }
}

控制台输出结果

pool-1-thread-2---->> 执行了任务2
pool-1-thread-1---->> 执行了任务0
pool-1-thread-3---->> 执行了任务3
pool-1-thread-1---->> 执行了任务4

由于任务1在线程池中等待时间最长,因此任务1被丢弃。

案例演示4:演示ThreadPoolExecutor.CallerRunsPolicy任务处理策略

public class ThreadPoolExecutorDemo04 {
    public static void main(String[] args) {
​
        /**
         * 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s
         */
        ThreadPoolExecutor threadPoolExecutor;
        threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS ,
                new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.CallerRunsPolicy());
​
        // 提交5个任务
        for(int x = 0 ; x < 5 ; x++) {
            threadPoolExecutor.submit(() -> {
                System.out.println(Thread.currentThread().getName() + "---->> 执行了任务");
            });
        }
    }
}

控制台输出结果

pool-1-thread-1---->> 执行了任务
pool-1-thread-3---->> 执行了任务
pool-1-thread-2---->> 执行了任务
pool-1-thread-1---->> 执行了任务
main---->> 执行了任务

通过控制台的输出,我们可以看到次策略没有通过线程池中的线程执行任务,而是直接调用任务的run()方法绕过线程池直接执行。

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

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

相关文章

安全渗透测试基础之漏洞扫描工具之Nessus使用介绍

前置条件:Nessus工具使用前要确保工具是服务状态 systemctl start nessusd.service 启动nessus服务 systemctl status nessusd.service 查看nessus服务状态 1.配置扫描模板 2.新增高级扫描 2.1 设置日程表: 2.2设置邮件收件人(可选): 2.3主机发现: 2.

软件设计模式系列之二十一——观察者模式

1 观察者模式的定义 观察者模式&#xff08;Observer Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许对象之间建立一对多的依赖关系&#xff0c;当一个对象的状态发生变化时&#xff0c;所有依赖于它的对象都会得到通知并自动更新。这个模式也被称为发布-订阅模式…

Android stdio的Gradle菜单栏无内容问题的解决方法

右边Gradle菜单栏里没有Tasks选项内容的问题 正常情况↓ 如果这个问题如果无法解决的话&#xff0c;Gradle打包就只能通过控制台输入命令来解决&#xff0c;但这无疑是把简单问题复杂化了&#xff0c;我们来看看怎么解决这个问题吧。 这里有几个方法提供&#xff0c;可以自行选…

Ipa Guard软件介绍:启动界面和功能模块全解析,保护你的iOS应用源码

ipaguard界面概览 ipaguard界面分左右2块&#xff1a;左边菜单导航栏&#xff0c;右边的功能区 左侧菜单&#xff1a;按模块分成启动界面&#xff0c;代码模块&#xff0c;文件模块&#xff0c;重签名与测试模块 右侧主功能区会随着功能变化&#xff0c;但是整体分3块&#xf…

Webpack 基础入门以及接入 CSS、Typescript、Babel

一、什么是 Webpack Webpack 是一款 JS 模块化开发的技术框架&#xff0c;其运作原理是将多个 JS 文件关联起来构成可运行的应用程序。 Webpack 拥有丰富的 plugins / loaders 插件生态圈&#xff0c;可以让 js 识别不同的语言如 .css, .scss, .sass, .json, .xml, .ts, .vue…

揭秘:机构招生电子传单制作的五个黄金法则

机构招生微传单制作一直都是让很多人在意的事情。一款好的微传单不仅可以吸引更多的学生&#xff0c;还可以省去很多招生工作的时间和精力。但是&#xff0c;很多人却不知道如何制作一款精美的微传单。下面就让我们来学习一下如何制作一款机构招生的微传单吧。 首先&#xff0c…

聊聊Go与依赖注入

如果你读过Robert C. Martin[1]的《敏捷软件开发&#xff1a;原则、模式与实践》[2](书的封皮见下图)&#xff0c;那么你一定知道经典的SOLID设计原则[3]中的“D”&#xff1a;依赖倒置原则&#xff08;Dependency Inversion Principle, DIP&#xff09;。 依赖倒置原则是面向对…

node版本问题:Error: error:0308010C:digital envelope routines::unsupported

前言 出现这个错误是因为 node.js V17及以后版本中最近发布的OpenSSL3.0, 而OpenSSL3.0对允许算法和密钥大小增加了严格的限制&#xff0c;可能会对生态系统造成一些影响. 在node.js V17以前一些可以正常运行的的应用程序,但是在 V17 及以后版本可能会抛出以下异常: 我重装系…

华为云云耀云服务器L实例评测|部署在线图表和流程图绘制工具drawio

华为云云耀云服务器L实例评测&#xff5c;部署在线图表和流程图绘制工具drawio 一、云耀云服务器L实例介绍1.1 云服务器介绍1.2 优势及其应用场景1.3 支持镜像 二、云耀云服务器L实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置 三、部署 drawio3.1 drawio 介绍3.2 Docker 环…

宝贝详情页制作的7个步骤,一看就懂

对于许多淘宝卖家来说&#xff0c;制作精美的详情页是吸引客户和提升销售的关键。然而&#xff0c;很多卖家可能会因为缺乏设计技能和经验而感到困惑。本文将为您揭示如何使用免费在线海报制作工具&#xff0c;如乔拓云&#xff0c;来制作具有吸引力的详情页&#xff0c;让您在…

基本数据类型在内存中存储形式

目录 内存分区模型 常量 与 变量 常量的定义 #define 与 const 区别 宏与const使用 修改const常量 整数类型 无符号整数 有符号整数 补码 内存分析 浮点数类型 float类型的IEEE编码 double类型的IEEE编码 基本的浮点数指令 数据类型转换分析 浮点数作为返回值…

嵌入式Linux应用开发-基础知识-第十六章GPIO和Pinctrl子系统的使用

嵌入式Linux应用开发-基础知识-第十六章GPIO和Pinctrl子系统的使用 第十六章 GPIO 和 Pinctrl 子系统的使用16.1 Pinctrl 子系统重要概念16.1.1 引入16.1.2 重要概念16.1.3 示例16.1.4 代码中怎么引用pinctrl 16.2 GPIO子系统重要概念16.2.1 引入16.2.2 在设备树中指定引脚16.2…

React 入门笔记

前言 国庆值班把假期拆了个稀碎, 正好不用去看人潮人海, 趁机会赶个晚集入门一下都火这么久的 React 前端技术. 话说其实 n 年前也了解过一丢丢来着, 当时看到一上来就用 JS 写 DOM 的套路直接就给吓退了, 扭头还去看 Vue 了&#x1f923;, 现在从市场份额来看, 确实 React 还…

分析各种表达式求值过程

目录 算术运算与赋值 编译器常用的两种优化方案 常量传播 常量折叠 加法 Debug编译选项组下编译后的汇编代码分析 Release开启02执行效率优先 减法 Release版下优化和加法一致&#xff0c;不再赘述 乘法 除法 算术结果溢出 自增和自减 关系运算与逻辑运算 JCC指…

决胜未来:解锁新科技趋势的无尽可能性

随着科技的飞速发展,我们的未来迈入一个新的时代。无论是人工智能、区块链、大数据、物联网还是机器学习等,这些技术正以前所未有的速度重塑我们的世界和生活。面对这些未来的科技趋势,我们能否抓住其中的机遇并寻找新的突破? 首先&#xff0c;我们来看一看人工智能。人工智能…

2023年中国工业脱水机行业供需分析:随着自动化和智能化技术的快速发展,销量同比增长4.9%[图]

工业脱水机行业是指专门从湿润的固体物料中去除水分的设备制造和相关服务。它广泛应用于食品加工、化工、制药、纺织、环保等行业&#xff0c;用于去除物料中的水分&#xff0c;提高产品质量和降低能耗。 工业脱水机行业分类 资料来源&#xff1a;共研产业咨询&#xff08;共研…

基于SpringBoot的师生共评的作业管理系统设计与实现

目录 前言 一、技术栈 二、系统功能介绍 课程管理 作业管理 作业互评 小组管理 作业管理 作业评分 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 随着信息互联网信息的飞速发展&#xff0c;无纸化作业变成了一种趋势&#xff0c;针对这个问题开发一个…

【Window10 】删除‘设备和驱动器’中的百度网盘、酷狗音乐、迅雷下载等

原因&#xff1a; 不想在设备里看到它。 解决方案&#xff1a; 打开cmd找到 HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\MyComputer\NameSpace\ 目录在 NameSpace 目录下找到对应的软件目录&#xff0c;删除即可&#xff08;挨个目录的点击进去看…

Java / MybatisPlus:JSON处理器的应用,在实体对象中设置对象属性,对象嵌套对象

1、数据库设计 2、定义内部的实体类 /*** Author lgz* Description* Date 2023/9/30.*/ Data // 静态构造staticName&#xff0c;方便构造对象并赋予属性 AllArgsConstructor(staticName "of") NoArgsConstructor ApiModel(value "亲友", description …

web:[极客大挑战 2019]PHP

题目 点进页面显示如下 根据页面提示&#xff0c;这个网站有备份文件&#xff0c;备份文件一般是bak文件格式&#xff0c;用dirsearch扫描 访问之后下载了一个文件 里面都是一些代码 在index.php中发现了一个类的文件&#xff0c;一个get传参&#xff0c;然后将传进的值进行反序…