3 - 线程池 Java内置的线程池 - ExecutorService

news2025/1/15 17:38:56

1、ExecutorService的介绍

ExecutorService 接口继承了Executor 接口,是Executor 的子接口。

Executors类 提供工厂方法用来创建不同类型的线程池。Executors是工具类,他提供对ThreadPoolExecutor的封装,会产生几种线程池供大家使用。

关于 Executor 、ExecutorService、Executors三者的区别

参考:Executor, ExecutorService 和 Executors 间的区别与联系 - 夏威夷8080 - 博客园

UML简要类图关系:

2、ExecutorService 的获取

注:返回值都是 ExecutorService

3种方法以及每种方法对应的一个重载的方法

如重载方法:newCachedThreadPool(ThreadFactory threadFactory):

ThreadFactory 也是一个接口,该接口允许我们自己去写实现类,在实现类的内部只需要创建一个线程对象即可——这样的话,相当于我们自己(程序员)可以控制线程池中每一个线程对象的创建。

 上面的 “无界队列方式” —— 是指任务累加或者说缓存的时候,是不限制数量的。

(1)newCachedThreadPool() 方法、

以及其重载的方法法 newCachedThreadPool(ThreadFactory threadFactory)

:创建一个默认的线程池对象,里面的线程可重用,且在第一次使用时才创建;

重载的:线程池中的所有线程都使用ThreadFactory来创建,这样的线程无需手动启动,自动执行。

代码示例:MyTest01

package com.zhoulz.demo02;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

/**
 * 练习 Executors 获取ExecutorService,然后调用方法,提交任务;
 * newCachedThreadPool()方法、
 *         以及其重载的方法newCachedThreadPool(ThreadFactory threadFactory)
 */
public class MyTest01 {
    public static void main(String[] args) {
        //test1(); // 下面test1()中的内容被提取出来了—— 快捷键Shift+Alt+M:提取方法Extract Method
                   // 提取的原因是因为方便测试。接下来要测试newCachedThreadPool()重载的方法

        test2();
    }

    //(1)练习newCachedThreadPool()方法
    private static void test1() {
        // 1、使用工厂类——获取线程池对象
        final ExecutorService es = Executors.newCachedThreadPool();
        // 然后就可以调用方法 :如提交任务、shutdown()方法关闭提交
        // 2、提交任务 —— 任务可以是Runnable类型的对象,也可以是Callable类型的对象
        // 为了代码的整洁性,下面单独写了一个任务类。(也可以写一个匿名内部类)
        // 可以通过循环的方式来提交任务
        for (int i = 1; i <= 10; i++) {
            es.submit(new MyRunnable(i));
        }
    }

    //(2)练习newCachedThreadPool()重载的方法—— (在里面直接传参数 ThreadFactory threadFactory)
    private static void test2() {
        // 1、使用工厂类——获取线程池对象
        final ExecutorService es = Executors.newCachedThreadPool(new ThreadFactory() {
            int n = 1;
             // 匿名内部类的内部,允许我们直接创建一个线程对象。所以下面return的时候,可以直接new
            @Override
            public Thread newThread(Runnable r) {
                //return null;
                // 直接 new,并把Runnable的对象作为一个实参
                return new Thread(r,"自定义的线程名称" + n++);
                // 这样,创建出来的线程 new Thread()就可以和这个任务Runnable r 绑定在一起了!!!
                // 然后就可以执行了。。。
                // 还可以指定线程名称、加编号 n ...见上
            }
        });

        // 2、提交任务(这里是通过匿名内部类的方式,见上面)
        for (int i = 1; i <= 10; i++) {
            es.submit(new MyRunnable(i));
        }
    }
}

/**
 * 任务类:包含一个任务编号,在任务中,打印出是哪一个线程正在执行任务
 */
class MyRunnable implements Runnable{
    // 为了对任务做一个区分,可以加一个编号,不能通过run()方法传进来,所以要通过构造方法
    private int id;

    public MyRunnable(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        // 获取线程的名称,打印一句话(这就是要执行的任务)
        String name = Thread.currentThread().getName();
        System.out.println(name + "执行了任务。。。" + id);
    }
}

结果:

test01:创建了10个线程

test02:—— 可见10个线程都是自定义创建的。

  

(2)newFixedThreadPool(int nThreads) 方法、以及其重载的方法 newFixedThreadPool(int nThreads, ThreadFactory threadFactory)

: 创建一个可重用固定线程数的线程池 ;

 重载的:创建一个可重用固定线程数的线程池且线程池中的所有线程都使用ThreadFactory来创建。

代码示例:MyTest02(基本同MyTest01)

package com.zhoulz.demo02;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

/**
 * 练习 Executors 获取ExecutorService,然后调用方法,提交任务;
 *(2)newFixedThreadPool(int nThreads)方法、以及其重载的方法
 *         newFixedThreadPool(int nThreads, ThreadFactory threadFactory)
 */
public class MyTest02 {
    public static void main(String[] args) {
        //test1();
        test2();
    }

    //(1)练习newFixedThreadPool(int nThreads)方法
    private static void test1() {
        // 1、使用工厂类——获取线程池对象
        final ExecutorService es = Executors.newFixedThreadPool(3);
        // 2、提交任务
        for (int i = 1; i <= 10; i++) {
            es.submit(new MyRunnable2(i));
        }
    }

    //(2)练习newCachedThreadPool()重载的方法
    private static void test2() {
        // 1、使用工厂类——获取线程池对象
        final ExecutorService es = Executors.newFixedThreadPool(3,new ThreadFactory() {
            int n = 1;
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r,"自定义的线程名称" + n++ +"-zlz");
            }
        });

        // 2、提交任务
        for (int i = 1; i <= 10; i++) {
            es.submit(new MyRunnable2(i));
        }
    }
}

/**
 * 任务类:包含一个任务编号,在任务中,打印出是哪一个线程正在执行任务
 */
class MyRunnable2 implements Runnable{
    private int id;

    public MyRunnable2(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        // 获取线程的名称,打印一句话
        String name = Thread.currentThread().getName();
        System.out.println(name + "执行了任务。。。" + id);
    }
}

结果:

test01:只创建了3个线程 

test02自定义的创建了3个线程 

这是匿名内部类中自定义的线程对象(任务?)加了“-zlz”作区分

结合如下输出结果,可见 执行的任务/线程是匿名内部类里的,而不是MyRunnable2里的。

 但是为什么没执行呢?(下面还是提交了任务 new MyRunnable2(i) 的啊)

(3)newSingleThreadExecutor()方法、

以及其重载的方法 newSingleThreadExecutor(ThreadFactory threadFactory)

:创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程;

重载的:创建一个使用单个 worker 线程的 Executor,且线程池中的所有线程都使用ThreadFactory来创建。

代码示例:MyTest03(基本同MyTest01、MyTest02)

package com.zhoulz.demo02;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

/**
 * 练习 Executors 获取ExecutorService,然后调用方法,提交任务;
 *(3)newSingleThreadExecutor()方法、以及其重载的方法
 *        newSingleThreadExecutor(ThreadFactory threadFactory)
 */
public class MyTest03 {
    public static void main(String[] args) {
        //test1();
        test2();
    }

    //(1)练习newFixedThreadPool(int nThreads)方法
    private static void test1() {
        // 1、使用工厂类——获取线程池对象
        final ExecutorService es = Executors.newSingleThreadExecutor();
        // 2、提交任务
        for (int i = 1; i <= 10; i++) {
            es.submit(new MyRunnable3(i));
        }
    }

    //(2)练习newCachedThreadPool()重载的方法
    private static void test2() {
        // 1、使用工厂类——获取线程池对象
        final ExecutorService es = Executors.newSingleThreadExecutor(new ThreadFactory() {
            int n = 1;
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r,"自定义的线程名称" + n++ +"-zlz");
            }
        });

        // 2、提交任务
        for (int i = 1; i <= 10; i++) {
            es.submit(new MyRunnable3(i));
        }
    }
}

/**
 * 任务类:包含一个任务编号,在任务中,打印出是哪一个线程正在执行任务
 */
class MyRunnable3 implements Runnable{
    private int id;

    public MyRunnable3(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        // 获取线程的名称,打印一句话
        String name = Thread.currentThread().getName();
        System.out.println(name + "执行了任务。。。" + id);
    }
}

结果:

test01:只创建了1个线程

test02:自定义的只创建了1个线程

submit()方法  —— 见上面的例子。

 3、shutdown()方法

以 newSingleThreadExecutor()方法、以及其重载的法 newSingleThreadExecutor(ThreadFactory threadFactory) 为例,进行举例。

代码示例:

package com.zhoulz.demo02;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

/**
 * 练习 Executors 获取ExecutorService,测试关闭线程池的方法;
 * (以newSingleThreadExecutor()-单线程的获取方法为例)
 * 关闭线程数池的两种形式:
 *          1)直接关闭;
 *          2)关闭之后不再接收新的任务,以前的任务还会继续执行;
 */
public class MyTest04 {
    public static void main(String[] args) {
        //test1();
        test2();
    }

    //(1)练习newFixedThreadPool(int nThreads)方法
    private static void test1() {
        // 1、使用工厂类——获取线程池对象
        final ExecutorService es = Executors.newSingleThreadExecutor();
        // 2、提交任务
        for (int i = 1; i <= 10; i++) {
            es.submit(new MyRunnable4(i));
        }
        // 3、关闭线程池,仅仅是不再接收新的任务,以前的任务还会继续执行
        es.shutdown(); // 结果:1个线程把10个任务执行完了
        // 验证关闭线程池之后,还能不能再继续提交新的任务?结果:是不可以的
        //es.submit(new MyRunnable4(888));
        // 还有一个关闭方法 shutdownNow()—— 见test02
    }

    //(2)练习newCachedThreadPool()重载的方法
    private static void test2() {
        // 1、使用工厂类——获取线程池对象
        final ExecutorService es = Executors.newSingleThreadExecutor(new ThreadFactory() {
            int n = 1;

            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r,"自定义的线程名称" + n++ +"-zlz");
            }
        });

        // 2、提交任务
        for (int i = 1; i <= 10; i++) {
            es.submit(new MyRunnable4(i));
        }
        // 3、立刻关闭线程池,如果线程池中还有缓存的任务没有执行,则取消执行,并返回这些任务
        // 返回的集合怎么保存?用List集合进行保存。shutdownNow()方法的返回值就是List
        List<Runnable> list = es.shutdownNow();
        // 可以对List集合进行遍历。也可以直接输出,但是前提得在任务类/线程类MyRunnable4中进行toString()方法的重写
        // toString()方法重写了,但是结果还是java.util.concurrent.FutureTask@14ae5a5 ???
        System.out.println(list);
        // 此时,同样不能提交新的任务
    }
}

/**
 * 任务类:包含一个任务编号,在任务中,打印出是哪一个线程正在执行任务
 */
class MyRunnable4 implements Runnable{
    private int id;

    public MyRunnable4(int id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "MyRunnable4{" +
                "id=" + id +
                '}';
    }

    @Override
    public void run() {
        // 获取线程的名称,打印一句话
        String name = Thread.currentThread().getName();
        System.out.println(name + "执行了任务。。。" + id);
    }
}

4、ExecutorService 的获取 —— 3种方法的总结

1)newCachedThreadPool()性能优先模式

        线程的数量是不做限制的。每次有任务来的时候,都是以任务优先,即优先执行任务,可以理解为性能最大化。(前提是服务器的硬件能跟得上,服务器压力会比较大)

2)newFixedThreadPool()要求性能一般,服务器的配置不是太高。

规定线程的数量,可以让服务器的压力不这么大。

3)newSingleThreadExecutor()追求绝对的安全

:只考虑安全,不考虑性能。如银行转账等场景。

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

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

相关文章

derby 转 mysql

背景 nacos使用standalone方式启动&#xff0c;数据存储到内置的derby数据库中&#xff0c;现在要更改为集群启动&#xff0c;原数据要转成mysql。 执行过程 1.nacos内置的derby数据打包后放到本地 tar -cvf data.tar nacos/data/derby-data 2.通过idea添加Apache Derby&a…

【区块链】Ankr被黑引发的思考

机会 三明治交易、夹子机器人、抢跑、抢新、抢购、秒杀&#xff0c;相信这些词你都听说过了&#xff0c;区块链上的各种套利操作&#xff0c;基本上都有一个大前提&#xff0c;就是监听链上最新的未打包交易&#xff0c;才能在第一时间抢占先机。 前段时间Ankr被黑&#xff0…

进阶 - Git的自定义

Git的自定义 忽略特殊文件 有些时候&#xff0c;你必须把某些文件放到Git工作目录中&#xff0c;但又不能提交它们&#xff0c;比如保存了数据库密码的配置文件啦&#xff0c;等等&#xff0c;每次git status都会显示Untracked files ...&#xff0c;有强迫症的童鞋心里肯定不…

大学生网页制作期末作业——html+css+javascript+jquery旅游官网6页 html大学生网站开发实践作业 web网页设计实例作业

&#x1f468;‍&#x1f393;静态网站的编写主要是用 HTML DⅣV CSSJS等来完成页面的排版设计&#x1f469;‍&#x1f393;&#xff0c;一般的网页作业需要融入以下知识点&#xff1a;div布局、浮动定位、高级css、表格、表单及验证、js轮播图、音频视频Fash的应用、uli、下拉…

java alibaba fastjson自定义序列化反序列化(教你解决问题思路)

大家版本不一样方式可能不一样&#xff0c;我不管你的fastjson版本是哪个&#xff0c;按照我这个思路去弄就行 写一个JSONObject类&#xff0c;导入fastjson的JSONObject&#xff0c;然后CTRL鼠标左键点进去看JSONObject源码&#xff0c;然后点击IDEA的左上角select opened fil…

掘金量化如何精准选股?

说起掘金量化如何精准选股的这个问题&#xff0c;相信大家也很期待&#xff0c;就比如说我们在量化投资中&#xff0c;一方面对股票的涨跌是需要灵活的去判断&#xff0c;才能知晓这其中有哪些是我们应该选择的股票&#xff0c;但是要自己去观察&#xff0c;也是很麻烦&#xf…

【Python项目】毕业设计必备,Python基于面向对象+tkinter打造学生信息管理系统 | 附源码

前言 halo&#xff0c;包子们上午好 很多学计算机的小伙伴应该都知道&#xff0c;毕业设计是一个头疼的东西 今天的话小编这边给大家准备好了一个Python基于面向对象tkinter打造学生信息管理系统 这不是毕业设计必备项目 说实话操作起来还是有那么一点点的难度的&#xff0c;但…

Spring Boot源码学习:自动配置与自定义注解详解

入门 RestContrller :此注解标记的类下的 所有 方法均会返回一个 domain 对象以代替视图Controller、ResponseBody 的缩写使用 Jackson2 以及 MappingJackson2HttpMessageConverter 类自动转换对象为 JSONSpringBootApplication&#xff1a;快捷注释&#xff0c;包含以下内容C…

“新十条”来了,精准防控是为了“行稳致远”

文|螳螂观察 作者|松雅湖、小江 形势正在快速变化&#xff0c;朝越来越好的方向。 12月7日&#xff0c;国务院联防联控机制发布防疫“新十条”措施&#xff0c;指向精准&#xff0c;要求明确&#xff0c;既要疫情防控精准化、科学化&#xff0c;也要防疫成果“行稳致远”&am…

sentence Bert解读及代码示例

0-前序 Bert已经是相当6了&#xff0c;但在STS&#xff08;语义文本相似性&#xff09;任务中&#xff0c;需要将两个句子都输入到网络中&#xff0c;也就是说要过模型&#xff0c;这样计算量就大了。如下是文本相似性&#xff0c;并不是语义。 from transformers import Ber…

RabbitMQ、RocketMQ、Kafka 三大组件详细教程,一文带你学完全部知识

RabbitMQ RabbitMQ各组件的功能 Broker &#xff1a;一个RabbitMQ实例就是一个BrokerVirtual Host &#xff1a;虚拟主机。相当于MySQL的DataBase&#xff0c;一个Broker上可以存在多个vhost&#xff0c;vhost之间相互隔离。每个vhost都拥有自己的队列、交换机、绑定和权限机…

项目执行管理的8个步骤

常言道&#xff1a;说起来容易&#xff0c;做起来难。在项目执行过程中&#xff0c;许多事情可能会出错。这就是为什么执行过程中的监控和跟踪很重要。 项目执行&#xff0c;如同项目管理一样&#xff0c;被分解成若干步骤&#xff0c;以确保你不会忽视任何关键的东西。八个项…

如何查看Chrome浏览器的页面缓存内容【详细教程】

如何查看浏览器页面缓存内容——代码&控制台知识调用前言引入控制台输入代码查看在控制台application查看知识调用 文章可能需要用到的知识&#x1f525;&#x1f525;&#x1f525;浏览器缓存有哪些&#xff08;通用缓存有哪些&#xff09; 前言引入 浏览器有多种缓存&a…

C++/Qt编程规范

文章目录一、头文件规范二、变量命名三、信号和槽四、注释一、头文件规范 在编码中对头文件的引用&#xff0c;建议按照以下顺序进行&#xff1a; 1)类定义头文件 2)其他库头文件 a)Qt模块头文件 b)XTAL平台框架模块头文件 c)第三方库头文件 d)自定义公共库头文件 二、变量命…

Linux7层负载均衡-Haproxy

Linux7层负载均衡 概述&#xff1a; 是一款高性能的负载均衡软件。 因为其专注于负载均衡这一些事情&#xff0c; 因此与nginx比起来在负载均衡这件事情上做更好&#xff0c;更专业。 特点&#xff1a; •支持tcp / http 两种协议层的负载均衡&#xff0c;使得其负载均衡功能…

【KNN分类】基于模拟退火优化KNN、蝗虫算法优化KNN实现数据分类附matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

web课程设计网页制作:基于html实现传统文化壁画手工艺带psd设计图(15页)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

【Day3】每日学Java--》详解java中的static成员

大家好&#xff0c;我是良辰丫&#xff0c;今天我和大家分享的是java中的static成员&#xff0c;static说难不难&#xff0c;说简单也不简单&#xff0c;主要是细节&#xff0c;很多情况下&#xff0c;你使用了static&#xff0c;却不知道为什么会报错&#xff0c;嘿嘿嘿&#…

【推免攻略】五.2022年北交计算机学院夏令营、预推免保研经验

欢迎订阅本专栏:《北交计算机保研经验》 订阅地址:https://blog.csdn.net/m0_38068876/category_10779337.html 【推免攻略】一.北交计算机学院夏令营、预推免攻略【推免攻略】二.联系导师的前期准备及注意事项【推免攻略】三.2020年北交计算机学院夏令营、预推免保研经验【推…