Java多线程技术11——ThreadPoolExecutor类的使用1-备份

news2024/9/29 19:30:15

 1 概述

        ThreadPoolExecutor类可以非常方便的创建线程池对象,而不需要程序员设计大量的new实例化Thread相关的代码。

2 队列LinkedBlockingQueue的使用

public class Test1 {
    public static void main(String[] args) {
        LinkedBlockingQueue queue = new LinkedBlockingQueue();
        queue.add("线程1");
        queue.add("线程2");
        queue.add("线程3");
        System.out.println(queue.poll()+" " + queue.size());
        System.out.println(queue.poll()+" " + queue.size());
        System.out.println(queue.poll()+" " + queue.size());
    }
}

        LinkedBlockingQueue队列最简单的使用就像ArrayList一样,使用add()保存数据,使用poll()获取数据。从上面的运行结果可以发现,LinkedBlockingQueue队列的容量好像是可以扩充的,其实并不是这样,因为在构造方法时传入了Integer的最大值,源代码如下:

    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }

        所以从本质上讲, LinkedBlockingQueue队列是有界的,下面验证其有界的实现:

public class Test2 {
    public static void main(String[] args) {
        LinkedBlockingQueue queue = new LinkedBlockingQueue(2);
        queue.add("线程1");
        queue.add("线程2");
        queue.add("线程3");
       
    }
}

        最大容量不能超过2。

3 ArrayBlockingQueue队列的使用

         ArrayBlockingQueue队列在实例化时必须传入初始容量,并且容量不可以扩充,超出初始容量就出现异常。验证异常情况

public class Test3 {
    public static void main(String[] args) {
        ArrayBlockingQueue queue = new ArrayBlockingQueue(3);
        queue.add("线程1");
        queue.add("线程2");
        queue.add("线程3");
        queue.add("线程4");
    }
}

        正常使用

public class Test4 {
    public static void main(String[] args) {
        ArrayBlockingQueue queue = new ArrayBlockingQueue(5);
        queue.add("线程1");
        queue.add("线程2");
        queue.add("线程3");
        queue.add("线程4");
        System.out.println(queue.poll()+" " + queue.size());
        System.out.println(queue.poll()+" " + queue.size());
        System.out.println(queue.poll()+" " + queue.size());
        System.out.println(queue.poll()+" " + queue.size());
    }
}

 4 SynchronousQueue队列的使用

         SynchronousQueue队列并不存储任何数据,通过该队列可以在2个线程之间直接传送数据。

public class Test5 {
    private static SynchronousQueue queue = new SynchronousQueue();

    public static void main(String[] args) {
        Thread put = new Thread(){
            public void run(){
                try {
                    for (int i = 0; i < 5; i++) {
                        String putString = "线程"+Math.random();
                        queue.put(putString);
                        System.out.println("putstring = " + putString);
                        Thread.sleep(1000);
                    }
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            };
        };
        put.start();
        Thread get = new Thread(){
            public void run(){
                try {
                    for (int i = 0; i < 5; i++) {
                        String takeString = "" + queue.take();
                        System.out.println("takeString = " + takeString );
                        Thread.sleep(1000);
                    }
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            };
        };
        get.start();
    }
}

        通过上面3个队列进行实验,可以分析以下特点:

        1、LinkedBlockingQueue和ArrayBlockingQueue可以存储多个数据,容量是有界限的。

        2、SynchronousQueue不可以存储多个数据,没有容量的概念。

4 构造方法参数详解

        ThreadPoolExecutor类最常使用的构造方法如下:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

         解释如下:

        1、corePoolSize:池中至少要保留的线程数,该属性就是定义corePool核心池的大小。

        2、maximumPoolSize:池中允许的最大线程数,maximumPoolSize包含corePoolSize。

        3、keepAliveTime:当线程数量大于corePoolSize值时,在没有超过执行的时间内是不能从线程池中将空闲线程删除的,如果超过这个时间单位,则删除空闲线程。“能删除的空闲线程”范围是corePoolSize~maximumPoolSize之间,也就是corePool之外的线程。

        4、unit:keepAliveTime参数的时间单位。

        5、workQueue:执行前用于保持任务的队列。此队列仅保持由execute方法提交的Runnable任务。

        注意:所谓的空闲线程就是没有执行任务的线程,不管这个线程在哪里,只要不执行任务,就是空闲的。下面对这些参数进行分析:

        A 代表execute(runnable)要执行的task任务的数量,如下图

        B 代表corePoolSize数量,如下图

          

        C 代表maximumPoolSize数量,如下图

        D 代表A-B(假设A>B)的值

        构造方法中5个参数之间都有关联关系,但从使用效果来讲,不同类型的队列能影响ThreadPool线程池执行的行为,所以后面的分析过程就以LinkedBlockingQueue和SynchronousQueue为主线,总结如下。

        4.1 使用无参LinkedBlockingQueue队列的情况

        注意,使用无参new LinkedBlockingQueue队列的特点就是只使用核心池中的线程执行任务。

        (1)如果A<=B,立即在corePool核心池中创建线程并运行任务,这些任务并不会放入LinkedBlockingQueue中,构造方法参数maximumPoolSize、keepAliveTime和unit将会被忽略。

        (2)如果A > B&& A <= C,构造方法参数maximumPoolSize、keepAliveTime和unit将会被忽略,并把D放入LinkedBlockingQueue中等待被核心池中的线程执行。

        (3)如果A > C ,构造方法参数maximumPoolSize、keepAliveTime和unit将会被忽略,并把D放入LinkedBlockingQueue中等待被核心池中的线程执行。

        4.2 使用SynchronousQueue队列的情况

        (1)如果A<=B,立即在corePool核心池中创建线程并运行任务,这些任务并不会放入SynchronousQueue中,构造方法参数maximumPoolSize、keepAliveTime和unit将会被忽略。

        (2)如果A > B&& A <= C,则构造方法参数maximumPoolSize、keepAliveTime和unit有效,并且拿上创建最多C个线程运行这些任务,而不把D放入SynchronousQueue队列中,D执行完任务后在指定的keepAliveTime时间发生超时时,将D进行清除,如果D在keepAliveTime时间之后未完成任务,则在D完成任务后进行清除。

        4.3 使用new LinkedBlockingQueue(xxx)队列有参的情况下。其中,参数xxx代表队列的最大存储长度。

        注意,使用有参new LinkBlockingQueue(xxx)队列的执行特点时核心池中的线程和maximumPoolSize - corePoolSize 现成有可能一起执行任务,也就是最多执行任务的线程数量就是maximumPoolSize。另外在使用有参new LinkedBlockingQueue(xxx)队列时,执行的流程是先判断corePoolSize大小够不够,如果不够则向new LinkedBlockingQueue(xxx)队列中存储,如果new LinkedBlockingQueue(xxx)队列中放不下,则将剩余的任务尝试向C - B 中存放,如果C - B放不下,就报异常。

        (1)如果 A <= B,立即在corePool核心池中创建线程并运行任务,这些任务并不会放入LinkedBlockingQueue中,构造方法参数maximumPoolSize、keepAliveTime和unit将会被忽略。

        (2)如果 A > B && (A - B) <=xxx,立即在corePoolSize核心池中创建线程并运行任务,构造方法参数maximumPoolSize、keepAliveTime和unit将会被忽略,并把(A - B)方法LinkedBlockingQueue队列中等待被核心池中的线程执行。

5 shutdown()和shutdownNow()

        public void shutdown()方法的作用是使当前未执行完的任务继续执行,而队列中未执行的任务也会继续执行,不删除队列中的任务,不再允许添加新的任务,同时shutdown()方法不会阻塞。

        public List<Runnable> shutdownNow()方法的作用是使当前未执行完的任务继续执行,而队列中未执行的任务不再执行,删除队列中的任务,不再允许添加新的酿热物,同时shutdownNow()方法不会阻塞。

public class MyRunnable1 implements Runnable {
    @Override
    public void run() {
        try {
            System.out.println("开始 "+ Thread.currentThread().getName() + " " + Utils.data(System.currentTimeMillis()));
            Thread.sleep(4000);
            System.out.println("结束 "+ Thread.currentThread().getName() + " " + Utils.data(System.currentTimeMillis()));
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
public class Test1 {
    public static void main(String[] args) {
        MyRunnable1 myRunnable1 = new MyRunnable1();
        ThreadPoolExecutor executor = new ThreadPoolExecutor(7,10,0L, TimeUnit.SECONDS,new LinkedBlockingDeque<>());
        System.out.println("main end");
    }
}

       

         线程池中没有任何的任务执行,继续实验。

public class Test2 {
    public static void main(String[] args) {
        MyRunnable1 myRunnable1 = new MyRunnable1();
        ThreadPoolExecutor pool = new ThreadPoolExecutor(7,10,0L, TimeUnit.SECONDS,new LinkedBlockingDeque<>());
        pool.execute(myRunnable1);
        System.out.println("main end");
    }
}

            

        任务执行完成后,线程池继续等待新的任务。

public class Test3 {
    public static void main(String[] args) {
        MyRunnable1 myRunnable1 = new MyRunnable1();
        ThreadPoolExecutor pool = new ThreadPoolExecutor(7,10,0L, TimeUnit.SECONDS,new LinkedBlockingDeque<>());
        pool.execute(myRunnable1);
        pool.shutdown();
        System.out.println("main end");
    }
}

 

        程序运行的效果是main线程输出“mian end”后,main线程立即销毁,线程池在4秒后销毁,进进程结束。

public class Test4 {
    public static void main(String[] args) throws InterruptedException {
        MyRunnable1 myRunnable1 = new MyRunnable1();
        ThreadPoolExecutor pool = new ThreadPoolExecutor(2,9999,9999L, TimeUnit.SECONDS,new LinkedBlockingDeque<>());
        pool.execute(myRunnable1);
        pool.execute(myRunnable1);
        pool.execute(myRunnable1);
        pool.execute(myRunnable1);
        Thread.sleep(1000);
        pool.shutdown();
        pool.execute(myRunnable1);
        System.out.println("main end");
    }
}

 

        从运行结果可知,程序执行了4个任务,最后一个任务抛出异常,因为执行了shutdown()方法不能添加新的任务,这个实验也证明执行shutdown方法后未将队列中的任务删除,直到全部任务运行结束。

        下面验证shutdownNow()方法。

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("开始 "+ Thread.currentThread().getName() + " " + Utils.data(System.currentTimeMillis()));
        for (int i = 0; i < Integer.MAX_VALUE / 50; i++) {
            String newString = new String();
            Math.random();
            Math.random();
            Math.random();
            Math.random();
            Math.random();
            Math.random();
        }
        System.out.println("结束 "+ Thread.currentThread().getName() + " " + Utils.data(System.currentTimeMillis()));
    }
}

public class Test1 {
    public static void main(String[] args) throws InterruptedException {
        MyRunnable myRunnable = new MyRunnable();
        ThreadPoolExecutor pool = new ThreadPoolExecutor(2,9999,9999L, TimeUnit.SECONDS,new LinkedBlockingDeque<>());
        pool.execute(myRunnable);
        pool.execute(myRunnable);
        pool.execute(myRunnable);
        pool.execute(myRunnable);
        Thread.sleep(1000);
        pool.shutdownNow();
        System.out.println("main end");
    }
}

        从控制台可以看出,2个任务被成功执行,其余两个任务被取消运行,并且进程销毁。

public class Test2 {
    public static void main(String[] args) throws InterruptedException {
        MyRunnable myRunnable = new MyRunnable();
        ThreadPoolExecutor pool = new ThreadPoolExecutor(2,9999,9999L, TimeUnit.SECONDS,new LinkedBlockingDeque<>());
        pool.execute(myRunnable);
        pool.execute(myRunnable);
        pool.execute(myRunnable);
        pool.execute(myRunnable);
        Thread.sleep(1000);
        pool.shutdownNow();
        pool.execute(myRunnable);
        System.out.println("main end");
    }
}

 

        控制台信息代表2个任务被成功执行,其余2个任务被取消运行,而最后一个任务则拒绝执行,抛出异常,进程最后会被销毁。

6 List<Runnable> shutdownNow()方法的返回值 

        在调用List<Runnable> shutdownNow()方法后,队列中的任务被取消运行,shutdownNow()方法的返回值是List<Runnable>,List对象存储的是还未运行的任务,也就是被取消掉的任务,下面进行验证。

public class MyRunnableA implements Runnable{
    private String username;

    public MyRunnableA(String username) {
        this.username = username;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public void run() {
        for (int i = 0; i < Integer.MAX_VALUE / 500; i++) {
            String newString1 = new String();
            String newString5 = new String();
            String newString6 = new String();
            String newString7 = new String();
            Math.random();
            Math.random();
            Math.random();
        }
        System.out.println(Thread.currentThread().getName() + "任务完成");
    }
}
public class Run1 {
    public static void main(String[] args) {
        try {
            MyRunnableA a1 = new MyRunnableA("A1");
            MyRunnableA a2 = new MyRunnableA("A2");
            MyRunnableA a3 = new MyRunnableA("A3");
            MyRunnableA a4 = new MyRunnableA("A4");
            ThreadPoolExecutor pool = new ThreadPoolExecutor(2,10,30, TimeUnit.SECONDS,new LinkedBlockingDeque<>());
            pool.execute(a1);
            pool.execute(a2);
            pool.execute(a3);
            pool.execute(a4);
            Thread.sleep(1000);
            List<Runnable> list = pool.shutdownNow();
            for (int i = 0; i < list.size(); i++) {
                MyRunnableA a = (MyRunnableA) list.get(i);
                System.out.println(a.getUsername() + "任务取消");
            }
            System.out.println("main end");
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

 

        有两个任务被取消。

7 shutdown()和shutdownNow()与中断

        如果正在执行的任务使用if(Thread.currentThread().isInterrupted() == true)和throw new InterruptedException()判断任务是否中断,那么在嗲用shutdown()后任务并不会被中断而是继续运行,当调用shutdownNow()方法后会将任务立即中断。

public class MyRunnableA implements Runnable{
    private String username;

    public MyRunnableA(String username) {
        this.username = username;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public void run() {
        try {
            while(true){
                if(Thread.currentThread().isInterrupted() == true){
                    throw new InterruptedException();
                }
            }
        }catch (InterruptedException e){
            e.printStackTrace();
            System.out.println("任务: " + username + "被中断");
        }
    }
}
public class Run1 {
    public static void main(String[] args) throws InterruptedException {
        MyRunnableA a1 = new MyRunnableA("a1");
        ThreadPoolExecutor pool = new ThreadPoolExecutor(2,10,30, TimeUnit.SECONDS,new LinkedBlockingDeque<>());
        pool.execute(a1);
        Thread.sleep(2000);
        pool.shutdown();
        System.out.println("main end");
    }
}

        程序运行后,线程池的任务并未中断,而是会继续运行。

public class Run2 {
    public static void main(String[] args) throws InterruptedException {
        MyRunnableA a1 = new MyRunnableA("a1");
        ThreadPoolExecutor pool = new ThreadPoolExecutor(2,10,30, TimeUnit.SECONDS,new LinkedBlockingDeque<>());
        pool.execute(a1);
        Thread.sleep(2000);
        pool.shutdownNow();
        System.out.println("main end");
    }
}

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

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

相关文章

字节跳动基础架构SRE-Copilot获得2023 CCF国际AIOps挑战赛冠军

近日&#xff0c;2023 CCF国际AIOps挑战赛决赛暨“大模型时代的AIOps”研讨会在北京成功举办&#xff0c;活动吸引了来自互联网、运营商、科研院所、高校、软硬件厂商等领域多名专家学者参与&#xff0c;为智能运维的前沿学术研究、落地生产实践打开了新思路。决赛中&#xff0…

leetcode算法题之记忆化搜索总结

记忆化搜索&#xff0c;可以理解为带备忘录的递归&#xff0c;方便进行剪枝&#xff0c;是一种以空间换时间的策略。 本章目录 1.斐波那契数2.不同路径3.最长递增子序列4.猜数字大小II5.矩阵中的最长递增路径 1.斐波那契数 斐波那契数 class Solution { public://递归int f…

GitEE-GitHub实现加速访问与下载项目

gitee域名&#xff1a;https://gitee.com gitee域名&#xff1a;https://github.com 一、从github导出项目到gitee上面&#xff0c;从而实现加速访问与下载 gitee和github都有同步其他仓库的功能&#xff0c;比如码云上就能直接从github或gitlab中导入&#xff1b; 只需要填…

路由器02_静态路由DHCP

一、静态路由 &#xff11;、静态路由特点 由管理员手工配置&#xff0c;是单向的&#xff0c;缺乏灵活性 &#xff12;、默认路由 默认路由是一种比较特殊静态路由&#xff0c;一般用于末节&#xff08;末梢&#xff09;网络&#xff0c;直接指定目标为任何地方 二、静态…

墨墨智库正式上线:开启您的AI智慧之旅

在这个由数据驱动的时代&#xff0c;AI技术正迅速改变我们的工作和生活方式。有没有想过一个平台可以为您提供所有AI相关资源的便捷访问&#xff1f;这就是「墨墨智库」的使命。我们非常高兴地宣布&#xff0c;经过精心准备和期待&#xff0c;「墨墨智库」现已正式上线&#xf…

深入理解C指针

深入理解C指针 ​#C语言 #​ #C指针 #​ 1 认识指针 指针&#xff1a;一个存放内存地址的变量 1.1 指针和内存 ​​ ‍ 阅读指针声明时候&#xff0c;可以选择倒过来读&#xff0c;会更容易理解。 指针被赋值为NULL时候&#xff0c;会被解释为二进制0. void指针 具有和…

docker 安装elasticsearch、kibana、cerebro

安装步骤 第一步安装 docker 第二步 拉取elasticsearch、kibana、cerebro 镜像 docker pull docker.elastic.co/elasticsearch/elasticsearch:7.10.2 docker pull docker.elastic.co/kibana/kibana:7.10.2 docker pull lmenezes/cerebro:latest第三步、创建 容器 创建e…

SQL Server从0到1——写shell

xp_cmdshell 查看能否使用xpcmd_shell&#xff1b; select count(*) from master.dbo.sysobjects where xtype x and name xp_cmdshell 直接使用xpcmd_shell执行命令&#xff1a; EXEC master.dbo.xp_cmdshell whoami 发现居然无法使用 查看是否存在xp_cmdshell: EXEC…

深入理解计算机系统(1):开始

计算机系统是由硬件和系统软件组成的&#xff0c;它们共同工作来运行应用程序。虽然系统的具体实现方式随着时间不断变化&#xff0c;但是系统内在的概念却没有改变。所有计算机系统都有相似的硬件和软件组件&#xff0c;它们又执行着相似的功能。 计算机系统 信息就是位上下…

❀记忆冒泡、选择和插入排序算法思想在bash里运用❀

目录 冒泡排序算法:) 选择排序算法:) 插入排序算法:) 冒泡排序算法:) 思想&#xff1a;依次比较相邻两个元素&#xff0c;重复的进行直到没有相邻元素需要交换&#xff0c;排序完成。 #!/bin/bash arr(12 324 543 213 65 64 1 3 45) #定义一个数组 n${#arr[*]} #获取数组…

非工程师指南: 训练 LLaMA 2 聊天机器人

引言 本教程将向你展示在不编写一行代码的情况下&#xff0c;如何构建自己的开源 ChatGPT&#xff0c;这样人人都能构建自己的聊天模型。我们将以 LLaMA 2 基础模型为例&#xff0c;在开源指令数据集上针对聊天场景对其进行微调&#xff0c;并将微调后的模型部署到一个可分享的…

【STM32】STM32学习笔记-定时器定时中断 定时器外部时钟(14)

00. 目录 文章目录 00. 目录01. 定时器中断相关API1.1 TIM_InternalClockConfig1.2 TIM_TimeBaseInit1.3 TIM_TimeBaseInitTypeDef1.4 TIM_ClearFlag1.5 TIM_ITConfig1.6 TIM_Cmd1.7 中断服务函数1.8 TIM_ETRClockMode2Config 02. 定时器定时中断接线图03. 定时器定时中断示例0…

每日算法打卡:数的范围 day 7

文章目录 原题链接题目描述输入格式输出格式数据范围输入样例&#xff1a;输出样例&#xff1a; 题目分析示例代码 原题链接 789. 数的范围 题目难度&#xff1a;简单 题目描述 给定一个按照升序排列的长度为 n 的整数数组&#xff0c;以及 q 个查询。 对于每个查询&#…

数字孪生在虚拟现实(VR)中的应用

数字孪生在虚拟现实&#xff08;VR&#xff09;中的应用为用户提供了更深入、沉浸式的体验&#xff0c;同时通过数字孪生技术模拟真实世界的物理实体。以下是数字孪生在VR中的一些应用&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发…

12月笔记

#pragma once 防止多次引用头文件&#xff0c;保证同一个&#xff08;物理意义上&#xff09;文件被多次包含&#xff0c;内容相同的两个文件同样会被包含。 头文件.h与无.h的文件&#xff1a; iostream是C的头文件&#xff0c;iostream.h是C的头文件&#xff0c;即标准的C头文…

电话号码信息收集工具:PhoneInfoga | 开源日报 No.137

sundowndev/phoneinfoga Stars: 11.2k License: GPL-3.0 PhoneInfoga 是一个用于扫描国际电话号码的信息收集框架&#xff0c;它允许用户首先收集基本信息 (如国家、地区、运营商和线路类型)&#xff0c;然后使用各种技术来尝试找到 VoIP 提供商或识别所有者。该工具与一系列必…

[MySQL]视图索引以及连接查询案列

目录 1.视图 1.1视图是什么 1.2视图的作用 1.3操作 1.3.1创建视图 1.3.2视图的修改 1.3.3删除视图 1.3.4查看视图 2.索引 2.1什么是索引 2.2为什么要使用索引 2.3索引的优缺点 2.3.1优点 2.3.2缺点 2.4索引的分类 3.连接查询案列 4.思维导图 1.视图 1.1视图是什么 视图…

代码随想录-刷题第四十九天

121. 买卖股票的最佳时机 题目链接&#xff1a;121. 买卖股票的最佳时机 思路&#xff1a;动态规划五步曲 dp[i][0] 表示第i天持有股票所得最多现金&#xff0c;dp[i][1] 表示第i天不持有股票所得最多现金。 一开始现金是0&#xff0c;那么加入第i天买入股票&#xff0c;现金…

深入了解 RDD

深入了解 RDD 案例 明确需求&#xff1a; 在访问日志中&#xff0c;统计独立IP数量 TOP10 查看数据结构&#xff1a; IP&#xff0c;时间戳&#xff0c;Http&#xff0c;Method&#xff0c;Url…… 明确编码步骤 取出IP&#xff0c;生成一个只有IP的数据集简单清洗统计IP出现…

Tomcat Notes: Deployment File

This is a personal study notes of Apache Tomcat. Below are main reference material. - YouTube Apache Tomcat Full Tutorial&#xff0c;owed by Alpha Brains Courses. https://www.youtube.com/watch?vrElJIPRw5iM&t801s 1、Tomcat deployment1.1、Two modes of …