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/299894.html

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

相关文章

四则运算 C语言xdoj20

问题描述&#xff1a; 输入两个整数和一个四则运算符&#xff0c;根据运算符计算并输出其运算结果&#xff08;和、差、积、商、余之一&#xff09;。注意做整除及求余运算时&#xff0c;除数不能为零。 输入说明&#xff1a; 使用scanf()函数输入两个整数和一个运算符&#xf…

【好书推荐】深入理解现代JavaScript

目录 推荐理由内容简介本书阅读对象为什么推荐这本书&#xff0c;看大佬们怎么说总结 T. J. Crowder是一位拥有30年经验的软件工程师。在他的整个职业生涯中&#xff0c;他至少有一半时间是在使用JavaScript从事开发工作。他经营着软件承包和产品公司Farsight Software。他经常…

工业协议转换网关:打破通信壁垒,实现设备互联

在工业自动化领域&#xff0c;各种设备和系统间的通信协议不尽相同&#xff0c;这给不同设备间的集成和数据交互带来了挑战。工业协议转换网关作为一种解决这一问题的关键设备&#xff0c;能够实现不同协议间的转换和数据传输&#xff0c;打破通信壁垒&#xff0c;提高设备的协…

2.8 EXERCISES

如果我们想使用每个线程来计算向量加法的一个输出元素&#xff0c;那么将线程/块索引映射到数据索引的表达式是什么&#xff1f; 答&#xff1a;C 假设我们想用每个线程来计算向量加法的两个&#xff08;相邻&#xff09;元素。将线程/块索引映射到i&#xff08;由线程处理的…

SpringSecurity集成JWT实现后端认证授权保姆级教程-数据准备篇

&#x1f341; 作者&#xff1a;知识浅谈&#xff0c;CSDN签约讲师&#xff0c;CSDN博客专家&#xff0c;华为云云享专家&#xff0c;阿里云专家博主 &#x1f4cc; 擅长领域&#xff1a;全栈工程师、爬虫、ACM算法 &#x1f492; 公众号&#xff1a;知识浅谈 &#x1f525;网站…

进阶学习——Linux系统安全及应用

目录 一、系统安全加固 1.账号安全基本措施 1.1系统账号清理 1.1.1延伸 1.2密码安全控制 1.3命令历史限制 1.4终端自动注销 二、使用su命令切换用户 1.用途及用法 2.密码验证 3.限制使用su命令的用户 4.查看su操作记录 5.sudo&#xff08;superuse do&#xff09;…

Linux下QT生成的(.o)、(.a)、(.so)、(.so.1)、(.so.1.0)、(.so.1.0.0)之间的区别

记录一下遇到的问题&#xff1a;Linux系统下Qt编译第三方动态库会生成多个.so文件&#xff0c;不了解的小伙伴可能很疑惑&#xff1a; &#xff08;1&#xff09;Linux 下 QT 生成的&#xff08;.o&#xff09;、&#xff08;.a&#xff09;和&#xff08;.so&#xff09;三个文…

如何向嵌入式设备中添加tcpdump工具

说明&#xff1a;tcpdump是一个在网络设备调试中一个非常重要的工具&#xff0c;它并不像hexdump等工具集成在busybox里面&#xff0c;也不像其他的软件一样只需要依赖linux标准的库就可以实现&#xff0c;它需要pcap相关的库和加密的相关库。 本文主要是基于realtek 83系列的…

APPnium 自动化实践 :第一步adb 连接手机

1. 下载安装 adb ,添加到环境变量。 ADB Download - Get the latest version of ADB and fastboot 2. 手机开启开发者模式 https://developer.huawei.com/consumer/cn/doc/quickApp-Guides/quickapp-open-developer-option-0000001137005543 3. adb 连接设备 【And…

网络安全与IP地址:构建数字世界的前沿堡垒

网络安全是当今数字社会中不可忽视的挑战之一。而IP地址&#xff0c;作为互联网通信的基础协议&#xff0c;既是数字化时代的桥梁&#xff0c;也是网络安全的关键节点。本文将剖析IP地址在网络安全领域的作用&#xff0c;以及如何利用其特性建立有效的网络安全策略。 IP地址&a…

【图神经网络导论】之第9章模型变体(刘知远)

第9章不同图类型的模型变体 文章目录 第9章不同图类型的模型变体9.1 有向图9.2 异构图9.3 带有边信息的图9.4 动态图9.5 多维图 第4章介绍的基础GNN模型"被用于处理无向图&#xff0c;这些图包含具有标签的节点&#xff0c;是最简单的图。然而&#xff0c;在现实世界中还有…

了解一下InternLM3

在 InternStudio 平台中选择 A100(1/4) 的配置&#xff0c;如下图所示镜像选择 Cuda11.7-conda&#xff0c;接下来打开刚刚租用服务器的进入开发机&#xff0c;并且打开其中的终端开始环境配置、模型下载和运行 demo。入开发机后&#xff0c;在页面的左上角可以切换 JupyterLab…

CSS 压重按钮 效果

<template><view class="cont"><div class="container"><div class="pane"><!-- 选项1 --><label class="label" @click="handleOptionClick(0)":style="{ color: selectedOption ==…

GD32 支持IAP的bootloader开发,使用串口通过Ymodem协议传输固件(附代码)

资料下载: https://download.csdn.net/download/vvoennvv/88713921 一、概述 关于IAP的原理和Ymodem协议&#xff0c;本文不做任何论述&#xff0c;本文只论述bootloader如何使用串口通过Ymodem协议接收升级程序并进行IAP升级&#xff0c;以及bootloader和主程序两个工程的配置…

shell编程学习(二)

变量的类型 预定义变量 $$ 当前进程PID $? 命令执行后的返回状态.0 为执行正确&#xff0c;非 0 为执行错误 $# 位置参数的数量 $* 所有位置参数的内容 …

Next.js 第一次接触

因为需要整个漂亮的在线文档&#xff0c;所以接触了next.js&#xff0c;因为对前端js本身不够熟悉&#xff0c;别说对react.js 又不会&#xff0c;时间又不允许深入研究&#xff0c;所以&#xff0c;为了加一个导航菜单&#xff0c;极其痛苦。 有点小bug&#xff0c;不过不影响…

《EnlightenGAN: Deep Light Enhancement withoutPaired Supervision》论文超详细解读(翻译+精读)

前言 最近学习低照度图像增强时读到这篇EnlightenGAN的论文觉得写得很有意思&#xff0c;讲故事的手法也很值得小白写论文时模仿&#xff0c;今天就来带大家读一下~ 目录 前言 ABSTRACT—摘要 翻译 精读 一、INTRODUCTION—简介 翻译 精读 二、RELATED WORKS—相关工…

Joplin配合teracloud进行多版本客户端分别笔记同步

最近瞎搜索joplin&#xff0c;意外在github上搜到plugins&#xff0c;插件仓库&#xff0c;里面有一个思维导图的插件我还是蛮喜欢的&#xff0c;结果下载后安装发现&#xff0c;我当前的Jopin的版本如下 &#xff08;Joplin 2.7.15 (prod, win32) 同步版本: 3 配置文件版本: 4…

Linux Ubuntu 20.04.6 Intel WiFi6 Ax411 1690i Ax1690i Killer 解决无线网卡识别不出来问题

项目场景&#xff1a; 网卡型号&#xff1a;英特尔 Killer™ Wi-Fi 6E AX1690 i/s ubuntu 版本 uname -a Linux kuanli 5.15.0-91-generic #101~20.04.1-Ubuntu SMP Thu Nov 16 14:22:28 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux cat /proc/version Linux version 5.15.0-9…

MySQL忘记密码,如何重置密码(Windows)

1. 停止MySQL服务 打开“服务”管理工具&#xff08;可以在开始菜单搜索“服务”或运行 services.msc&#xff09;。 找到你的MySQL服务&#xff0c;可能叫别的&#xff0c;但是应该都是mysql开头的。 鼠标右键停止运行它。 2. 跳过权限表启动 MySQL 打开命令提示符&#x…