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

news2024/9/28 23:33:34

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

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

相关文章

Activity启动流程

早就想写这个笔记用于记录这段知识&#xff0c;但是碍于太过庞大所以始终没有进行这段知识的整理 很多博客喜欢画一个时序图展示所有的流程&#xff0c;但是过于庞大&#xff0c;看起来有点吃力&#xff0c;这里我们画多个时序图来展示这个流程 1.app请求AMS启动Activity 在前…

特征工程筛选重要变量

特征筛选主要分为3个方法&#xff1a;过滤法、嵌入法&#xff08;经典的一些树模型比如xgboost&#xff09;、包裹法&#xff08;经典的RFECV&#xff0c;RFE递归特征消除法&#xff09; 过滤法更快速&#xff0c;但更粗糙。 包装法和嵌入法更精确&#xff0c;比较适合具体到算…

P1262 间谍网络

1、思路 阅读题目&#xff0c;发现有些间谍可以是被前面的点更新&#xff0c;也就是说&#xff0c;在一开始的时候&#xff0c;把能贿赂的人员从小到达排个序&#xff0c;再使用bfs算法&#xff0c;把他们能到达的人员的贿赂价钱设置为0。 有解的情况&#xff1a; 首先如果有…

JavaScript:Date 对象-时间日期

Date 对象-时间日期: - JS中所有的关于时间信息都需要通过Date对象来表示 // 创建一个Date对象 // 如果直接使用new Date()创建时间对象&#xff0c;它会默认创建一个表示代码执行时刻的对象var d new Date();// 如果希望创建一个指定的时间的Date的对象&#xff0c;需要传递…

python总结-装饰器

装饰器 装饰器解决日志问题&#xff08;分三个版本&#xff09;多个装饰器带参数的装饰器wraps装饰器内置装饰器property装饰器staticmethod装饰器classmethod装饰器 类装饰器缓存装饰器和计时装饰器综合练习 概念 装饰器来自 Decorator 的直译。什么叫装饰&#xff0c;就是装点…

【tkinter 电子时钟 实现时间日期 可实现透明 无标题栏】

下面是一个使用tkinter实现的简单的电子时钟&#xff0c;包括时间和日期的显示。该窗口是透明的&#xff0c;没有标题栏。 效果&#xff1a; import tkinter as tk from datetime import datetimedef update_time():now datetime.now()time_label.configure(textnow.strftim…

【一】使用vue-cli创建vue3的helloworld项目

不再推荐使用vue-cli命令创建vue3的项目&#xff0c;vue-cli 是 Vue 早期推出的一款脚手架&#xff0c;使用 webpack 创建 Vue 项目。后期推荐使用 create-vue&#xff0c;create-vue 是 Vue3 的专用脚手架&#xff0c;使用 vite 创建 Vue3 的项目(关注【二】使用create-vue创建…

操作系统期末复习笔记(持续更新..)

一、操作系统的基本概念 1.1 操作系统概念 控制和管理整个计算机系统的硬件与软件资源。合理地组织、调度计算机的工作与资源。为用户和其他软件提供方便接口与环境的程序集合。 1.2 操作系统的特征 特征&#xff1a;并发&#xff0c;共享&#xff0c;虚拟&#xff0c;异步…

每日算法打卡:数的三次方根 day 7

文章目录 原题链接题目描述输入格式输出格式数据范围输入样例&#xff1a;输出样例&#xff1a; 题目分析示例代码 原题链接 790. 数的三次方根 题目难度&#xff1a;简单 题目描述 给定一个浮点数 n&#xff0c;求它的三次方根。 输入格式 共一行&#xff0c;包含一个浮…

如何将手机中termux用电脑的vnc显示

在电脑中我们同样需要下载 vnc 这里填写手机上的 IP&#xff1a;端口号 我的是 10.11.166.219:5902 下面填名字然后 手机端 输入sshd开始ssh这边就可以连接啦

【ARM 处理器】程序存储详解

本篇文章主要介绍ARM处理器&#xff0c;Code, RO-data,RW-data,ZI-data 知识以及程序存储情况 目录 1. 专业词汇2. 程序存储3. 程序空间计算 1. 专业词汇 Code &#xff1a; 代码区&#xff0c;存储在 ROM 区域RO-data&#xff1a;Read Only data&#xff0c;即只读数据域&…

2024年虚拟DOM技术将何去何从?

从诞生之初谈起&#xff0c;从命令式到声明式&#xff0c;Web开发的演变之路 Web开发的起源与jQuery的统治 在Web开发的早期阶段&#xff0c;操作DOM元素主要依赖命令式编程。当时&#xff0c;jQuery因其易用性而广受欢迎。使用jQuery&#xff0c;开发者通过具体的命令操作DOM&…

qt自定义控件的封装

刚学了一个很有意思的东西,前面学了list,Tree,Table三大控件和一部分常用基础控件,但感觉没啥意思,就是用别人的直接用,刚学了一个自定义控件的封装,流程如下: 想把两个不相关的组件封装在一块,直接用ui不行,所以先新添加了qt设计师页面,新添加了一个SmallWidget *ui 在smal…

FCN-8s源码理解

FCN网络用于对图像进行分割&#xff0c;由于是全卷积网络&#xff0c;所以对输入图像的分辨率没有要求。本文重点对fcn8s.py中图像降采样和上采样后图像分辨率的变换进行理解。 相关知识 为准确理解图像分辨率的变换&#xff0c;对网络结构中影响图像分辨率变换的几个函数进行…

Linux基础命令@echo、tail、重定向符

目录 echo概念语法作用演示一演示二 反引号作用 tail概念语法作用不带选项&#xff0c;演示一带选项 -num&#xff0c;演示二带选项 -f &#xff0c; 持续跟踪 重定向符概念作用覆盖重定向&#xff0c;>演示一演示二 追加重定向&#xff0c;>>演示一演示二 总结 echo …

腾讯云取消免费10G CDN流量包:免费CDN时代结束

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 免费送了7-8年的腾讯云10G免费流量包&#xff0c;从2024年开始&#xff0c;停止赠送了!自此&#xff0c;国内绝大多数互联网大厂的CDN都开收费了! 大概从2016年开始&#xff0c;腾讯云为了抢夺CDN客户&#xff0…

CentOS使用docker安装mysql并使用navicat 远程链接

这篇文章没用开启mysql的挂载功能&#xff0c;如果想开启的话可以和我的下篇文章结合着看。 CentOS中开启mysql挂载-CSDN博客 docker在之前的文章中已经安装完成了 这里输入命令查询已被上传的MySQL镜像 docker search mysql这里stars代表点赞数&#xff0c;official代表官…

瓢虫目标检测数据集VOC格式400张

瓢虫&#xff0c;一种小巧玲珑、色彩鲜艳的昆虫&#xff0c;因其独特的形态和生态习性而受到广泛欢迎。 瓢虫的体型小巧&#xff0c;一般为圆球形&#xff0c;体色鲜艳&#xff0c;有红、黄、黑等多种颜色。它们通常有一个坚硬的外壳&#xff0c;可以保护自己不受天敌的侵害。…

一文讲透使用SPSS统计分析软件绘制双轴线图

双轴线图主要用来展示两个因变量和一个自变量的关系&#xff0c;并且两个因变量的数值单位不同时的情形。具体来说&#xff0c;双轴线图是指在一幅图上有一个横轴和两个纵轴&#xff0c;适用于三个变量。两个纵轴分别表示一个变量&#xff0c;横轴变量同时适用于两个纵轴上的变…

案例098:基于微信小程序的电子购物系统的设计与实现

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…