我的编程空间,编程开发者的网络收藏夹
学习永远不晚

从零开始学习 Java:简单易懂的入门指南之线程池(三十六)

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

从零开始学习 Java:简单易懂的入门指南之线程池(三十六)

线程池

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 线程池-参数详解

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=.%5Cimg%5C1591165506516.png&pos_id=img-0TZaH6p5-169366408644

public ThreadPoolExecutor(int corePoolSize,  int maximumPoolSize,  long keepAliveTime,  TimeUnit unit,  BlockingQueue<Runnable> workQueue,  ThreadFactory threadFactory,  RejectedExecutionHandler handler)    corePoolSize:   核心线程的最大值,不能小于0maximumPoolSize:最大线程数,不能小于等于0,maximumPoolSize >= corePoolSizekeepAliveTime:  空闲线程最大存活时间,不能小于0unit:           时间单位workQueue:      任务队列,不能为nullthreadFactory:  创建线程工厂,不能为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) {                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) {                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) {                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---->> 执行了任务2pool-1-thread-1---->> 执行了任务0pool-1-thread-3---->> 执行了任务3pool-1-thread-1---->> 执行了任务4

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

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

public class ThreadPoolExecutorDemo04 {    public static void main(String[] args) {                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()方法绕过线程池直接执行。

后记
👉👉💕💕美好的一天,到此结束,下次继续努力!欲知后续,请看下回分解,写作不易,感谢大家的支持!! 🌹🌹🌹

来源地址:https://blog.csdn.net/m0_59230408/article/details/132643519

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

从零开始学习 Java:简单易懂的入门指南之线程池(三十六)

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

从零开始学习 Java:简单易懂的入门指南之爬虫(十六)

爬虫 1.1 本地数据爬取1.2 网络数据爬取1.3 爬取数据练习1.4 按要求爬取1.5 贪婪爬取和非贪婪爬取1.6 String的split方法中使用正则表达式1.7 String类的replaceAll方法中使用正则表达式1.
2023-08-25

从零开始学习 Java:简单易懂的入门指南(三)

java基础知识 1.运算符和表达式运算符:表达式: 2.算术运算符练习:数值拆分 3.隐式转换概念:简单记忆:两种提升规则:取值范围从小到大的关系: 4.隐式转换的练习案例一:案例二:案例三:案例四:
2023-08-16

从零开始学习 Java:简单易懂的入门指南之方法(六)

java基础知识 1. 方法概述1.1 方法的概念 2. 方法的定义和调用2.1 无参数方法定义和调用2.3 无参数方法的练习 3. 带参数方法定义和调用3.1 带参数方法定义和调用3.2 形参和实参3.3 带参数方法练
2023-08-17

从零开始学习 Java:简单易懂的入门指南之MAth、System(十二)

常见API,MAth、System 1 Math类1.1 概述1.2 常见方法1.3 算法小题(质数)1.4 算法小题(自幂数) 2 System类2.1 概述2.2 常见方法 1 Math类 1.1 概述 tips
2023-08-18

从零开始学习 Java:简单易懂的入门指南之时间类(十七)

时间类 第一章 Date类1.1 Date概述1.2 Date常用方法 第二章 SimpleDateFormat类2.1 构造方法2.2 格式规则2.3 常用方法2.4 练习1(初恋女友的出生日期)2.5 练习2(秒杀活动)
2023-08-30

从零开始学习 Java:简单易懂的入门指南(一)

Java基础语法 1. 人机交互1.1 什么是cmd?1.2 如何打开CMD窗口?1.3 常用CMD命令1.4 CMD练习1.5 环境变量 2. Java概述1.1 Java是什么?1.2下载和安装1.2.1 下载1.2.2
2023-08-16

从零开始学习 Java:简单易懂的入门指南(二)

Java基础语法 1. 注释1.1使用的技巧1.2注意点 2. 关键字2.1 概念2.2 第一个关键字class 3. 字面量3.1区分技巧 4. 变量4.1 什么是变量?4.2 变量的定义格式4.2.1 格式详解
2023-08-19

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录