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

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");
    }
}

 

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

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

相关文章

LeetCode-移动零(283)

题目描述&#xff1a; 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 思路&#xff1a; 这里的思路跟以前做过的去重复数字的思路有点像&…

字符输入输出 C语言xdoj16

问题描述&#xff1a; 通过键盘输入5个大写字母&#xff0c;输出其对应的小写字母&#xff0c;并在末尾加上“&#xff01;”。 输入说明&#xff1a; 5个大写字母通过键盘输入&#xff0c;字母之间以竖线“|”分隔。 输出说明&#xff1a; 输出5个大写字母对应的小写字母&…

多线程模板应用实现(实践学习笔记)

出处&#xff1a;B站码出名企路 个人笔记&#xff1a;因为是跟着b站的教学视频以及文档初步学习&#xff0c;可能存在诸多的理解有误&#xff0c;对大家仅供借鉴&#xff0c;参考&#xff0c;然后是B站up阳哥的视频&#xff0c;我是跟着他学。大家有兴趣的可以到b站搜索。加油…

webpack的性能优化(一)——分包优化

1.什么是分包&#xff1f;为什么要分包&#xff1f; 默认情况下&#xff0c;Webpack 会将所有代码构建成一个单独的包&#xff0c;这在小型项目通常不会有明显的性能问题&#xff0c;但伴随着项目的推进&#xff0c;包体积逐步增长可能会导致应用的响应耗时越来越长。归根结底这…

【Linux】进程信号——进程信号的概念和介绍、产生信号、四种产生信号方式、阻塞信号、捕捉信号、阻塞和捕捉信号的函数

文章目录 进程信号1.进程信号的概念和介绍2.产生信号2.1通过终端按键产生信号2.2 调用系统函数向进程发信号2.3 由软件条件产生信号2.4硬件异常产生信号 3.阻塞信号3.1信号在内核中的表示3.2信号集操作函数3.3sigprocmask 4.捕捉信号4.1内核如何实现信号的捕捉4.2 sigaction 进…

【AI视野·今日Robot 机器人论文速览 第七十一期】Fri, 5 Jan 2024

AI视野今日CS.Robotics 机器人学论文速览 Fri, 5 Jan 2024 Totally 11 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Robotics Papers Machine Learning in Robotic Ultrasound Imaging: Challenges and Perspectives Authors Yuan Bi, Zhongliang Jiang, Felix D…

线性代数 --- 矩阵行列式的性质

Determinant det A|A| 矩阵的行列式是一个数&#xff0c;这个数能够反应一些关于矩阵的信息。行列式只对方阵有效。 若矩阵A为&#xff1a; 则A的行列式为&#xff1a; 性质1&#xff1a; 单位矩阵的行列式等于1 性质2&#xff1a;行与行之间的交换会改变det的正负号 以2x2单位…

Mybatis入门源码二:sql执行

后面开始分析sql执行的源码流程也就是这一部分 一、factory.openSession() 重点关注configuration.newExecutor这个方法&#xff0c;获取事务处理器比较简单&#xff0c;就是获取一个jdbc的事务管理器。 这个方法通过传入的执行器类型来创建不同的执行器&#xff0c;有simp…

x-cmd pkg | trdsql - 能对 CSV、LTSV、JSON 和 TBLN 执行 SQL 查询的工具

目录 简介首次用户技术特点竞品和相关作品进一步阅读 简介 trdsql 是一个使用 sql 作为 DSL 的强大工具: 采用 SQL 对 CSV、LTSV、JSON 和 TBLN 文件执行查询与 MySQL&#xff0c;Postgresql&#xff0c;Sqlite 的 Driver 协同&#xff0c;可以实现对应数据库的表与文件的 JO…

安全基础~信息搜集3

文章目录 知识补充APP信息搜集php开发学习理解漏洞 知识补充 端口渗透总结 python Crypto报错&#xff1a;https://blog.csdn.net/five3/article/details/86160683 APP信息搜集 1. AppInfoScanner 移动端(Android、iOS、WEB、H5、静态网站)信息收集扫描工具 使用教程 演示&…

超维空间M1无人机使用说明书——21、基于opencv的人脸识别

引言&#xff1a;M1型号无人机不仅提供了yolo进行物体识别&#xff0c;也增加了基于opencv的人脸识别功能包&#xff0c;仅需要启动摄像头和识别节点即可 链接: 源码链接 一、一键启动摄像头和人脸识别节点 roslaunch robot_bringup bringup_face_detect.launch无报错&#…

解决ImportError: Failed to import test module: sys.__init__

解决ImportError: Failed to import test module: sys.init 背景 学习通过文件夹执行测试脚本时&#xff0c;出现了错误&#xff1a;ImportError: Failed to import test module: sys.__init__ 解决过程 根据报错信息&#xff1a;sys is not a package大胆猜测可能是文件名…

爬虫-1-请求和响应

#无以规矩&#xff0c;不成方圆(&#xff89;_ _)&#xff89; <(_ _)> 请求和响应 案例实现

STLink下不了程序的解决办法

目录 1.检查物理接线是否正确 2.检查工程中用的引脚与这两个引脚是否有冲突 3.其次查看HAL_MspInit函数中是否使能SWJ 1.检查物理接线是否正确 2.检查工程中用的引脚与这两个引脚是否有冲突 stm32 swdio和swdclk引脚分别与stm32的PA13&#xff0c;PA14引脚相连 3.其次查看HA…

C++模板——(1)模板的概念

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 创造机会的人是勇者&#xff0c;等待机…

构建网络信息安全的中国方案 - 国密SSL协议介绍以及国密Nginx服务器部署

国密SSL协议 国密SSL协议指的是采用国密算法&#xff0c;符合国密标准的安全传输协议。简而言之&#xff0c;国密SSL就是SSL/TLS协议的国密版本。TLS协议定义有三个版本号&#xff0c;为0x0301、0x0302、0x0303&#xff0c;分别对应TLS 1.0、1.1、1.2。国密SSL为了避免冲突&am…

Spanner on a modern columnar storage engine 中文翻译

文章目录 0. 摘要1. 存储引擎2. 存储引擎迁移的挑战2.1 可靠性、可用性和数据完整性2.2 性能和成本2.3 复杂性 3. 迁移可靠性的系统原则方法3.1 可靠性原则和自动化架构3.2 迁移方案和按周迁移3.3 客户 部署感知 调度3.4 管理可靠性、可用性和性能 4. 项目管理和驱动指标概括 0…

在 Linux 中开启 Flask 项目持续运行

在 Linux 中开启 Flask 项目持续运行 在部署 Flask 项目时&#xff0c;情况往往并不是那么理想。默认情况下&#xff0c;关闭 SSH 终端后&#xff0c;Flask 服务就停止了。这时&#xff0c;您需要找到一种方法在 Linux 服务器上实现持续运行 Flask 项目&#xff0c;并在服务器…

【AI视野·今日Robot 机器人论文速览 第六十九期】Wed, 3 Jan 2024

AI视野今日CS.Robotics 机器人学论文速览 Wed, 3 Jan 2024 Totally 5 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Robotics Papers NID-SLAM: Neural Implicit Representation-based RGB-D SLAM in dynamic environments Authors Ziheng Xu, Jianwei Niu, Qingf…

Unity 一文掌握使用AddListener方法为组件事件添加监听器的方法

在Unity中&#xff0c;很多组件都带有事件&#xff0c;比如: Button组件&#xff1a;onClick() Toggle组件&#xff1a;On Value Changed(Boolean) Dropdown组件&#xff1a;On Value Changed(Int32) InputField组件&#xff1a;On Value Changed(String)、On End Edit(Stri…