首页 > 其他分享 >四种封装 ThreadPoolExecutor 的线程池的使用以及直接使用 ThreadPoolExecutor ,优缺点分析

四种封装 ThreadPoolExecutor 的线程池的使用以及直接使用 ThreadPoolExecutor ,优缺点分析

时间:2024-07-05 16:55:55浏览次数:21  
标签:execute 柜台 优缺点 submit 任务 线程 new ThreadPoolExecutor

池化思想:线程池、字符串常量池、数据库连接池
提高资源的利用率
下面是手动创建线程和执行任务过程,可见挺麻烦的,而且线程利用率不高。

  1. 手动创建线程对象
  2. 执行任务
  3. 执行完毕,释放线程对象

线程池的优点:

  • 提高线程的利用率
  • 提高程序的响应速度
  • 便于统一管理线程对象
  • 可以控制最大并发数
package chapter09;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.*;

public class Java05_Thread_Pool {
    public static void main(String[] args) {
        // TODO 线程 - 线程池
        // 所谓线程池,其实就是线程对象的容器
        // 可以根据需要,在启动时,创建一个或多个线程对象

        // 快速创建线程池的4种比较常见的方法
        // 它们都是对 ThreadPoolExecutor 的封装,那必然无法满足更复杂,更需要自定义功能的需求场景
        // 1. 创建固定数量的线程对象
        //    ExecutorService是线程服务对象
         ExecutorService executorService = Executors.newFixedThreadPool(3);
        // 2. 根据需求动态创建线程, 创建的线程可以重复使用,只是当目前线程不够了他会动态增加线程
        executorService = Executors.newCachedThreadPool();
        // 3. 单一线程
        executorService = Executors.newSingleThreadExecutor();
        // 4. 定时调度线程, 线程有3个,但是线程在什么时候执行我们可以去定义他
        executorService = Executors.newScheduledThreadPool(3);

        // 使用submit提交任务
        for (int i = 0; i < 5; i++) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("submit方式 " + Thread.currentThread().getName());
                }
            });
        }

        // 使用execute提交任务
        for (int i = 0; i < 5; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("execute方式 " + Thread.currentThread().getName());
                }
            });
        }

        executorService.shutdown(); // 关闭线程池

         // 如果你需要更多的控制和自定义选项,使用 ThreadPoolExecutor 可以满足更复杂的需求
        // 使用银行柜台服务的场景来解释 ThreadPoolExecutor 的每个参数
        ExecutorService executorService1 = new ThreadPoolExecutor(
                3,  // corePoolSize-核心线程数,即银行的初始柜台数,
                // 具体来说:银行一开始有 3 个柜台,每个柜台有一个员工(线程)在处理客户(任务)。这些柜台是常驻的,即使没有客户时也不会关闭

                5,  // maximumPoolSize-最大线程数,即银行在高峰期能开设的最大柜台数量
                // 在客户数量增加的时候,银行可以最多开设 5 个柜台来处理客户。超过这个数量的客户需要排队等候

                1L, // keepAliveTime-非核心线程的存活时间
                // 如果一个临时增加的柜台(超过核心柜台数的部分)空闲了 1 秒钟,那么这个柜台就会关闭(线程会被回收)。
                // 这就像银行在高峰期增加了柜台,但在高峰期过后,临时柜台会在短时间内关闭,以节省资源

                TimeUnit.SECONDS, // keepAliveTime's unit-存活时间的单位
                // 这里的单位是秒,表示非核心线程在空闲 1 秒后会被关闭,跟上面的 keepAliveTime 配合使用


                new ArrayBlockingQueue<>(3), // workQueue-等待队列,用于存放等待处理的任务
                // 假如银行有一个容量为 3 的等候区(队列),当所有柜台都在忙碌时,新的客户会在这个等候区等待。
                // 如果等候区也满了,那么将会触发下面handler的策略(下面是 拒绝策略)

                Executors.defaultThreadFactory(), // threadFactory-线程工厂,用于创建新线程
                // 这是一个工厂模式,负责为银行创建新的柜台(线程)。
                // Executors.defaultThreadFactory() 是默认的实现,它会创建一个新的线程来处理任务。


                new ThreadPoolExecutor.AbortPolicy() // handler-当线程池和等待队列都满了时如何处理新任务的策略
                // 如果所有的柜台(线程)和等候区(队列)都满了,再来新的客户时会执行拒绝策略。
                // AbortPolicy 会抛出 RejectedExecutionException,就像银行告诉新来的客户“我们现在无法处理您的请求,请稍后再试”。
        );

        /*
        ExecutorService类对象可以使用两种方法向线程池提交任务:submit 和 execute。
        execute 方法用于提交不需要返回结果的任务。它是 Executor 接口中的方法
            返回类型: void。execute 方法不返回任何结果。
            异常处理: 如果任务在执行过程中抛出未经检查的异常,该异常将直接传播到调用者所在的线程。
            适用场景: 适用于不需要返回结果的任务,比如简单的异步执行任务。

        submit 方法用于提交有返回结果的任务。它是 ExecutorService 接口中的方法。
            返回类型: Future<V>。submit 方法返回一个 Future 对象,可以通过该对象获取任务的执行结果或取消任务。
            异常处理: 如果任务在执行过程中抛出未经检查的异常,该异常将被捕获,并存储在返回的 Future 对象中。调用 Future.get() 方法时,会抛出 ExecutionException。
            适用场景: 适用于需要返回结果的任务,比如计算任务,或者你需要检查任务是否成功完成。

        通过这两种方法,你可以根据任务的具体需求来选择合适的提交方式。
        如果任务需要返回结果或需要检查执行状态,使用 submit;
        如果任务只是执行,不需要返回结果,使用 execute。
         */

        List<Future<Integer>> futureList = new ArrayList<>();
        // 使用submit提交任务,并且改变需要执行的Runnable任务来看看线程池有什么变化情况
        // 任务数i在6个以内,executorService1指向的线程池只开3个线程
        // 7个任务开四个线程, 8个任务开五个线程
        // 9个任务直接 Exception in thread "main" java.util.concurrent.RejectedExecutionException
        // 解释:
        // 3个任务:直接全部给到三个核心线程处理
        // 4~6个任务:三个核心线程处理其中3个任务,剩下的任务进入等待队列
        // 7个任务:三个核心线程处理其中3个任务,等待队列存放3个任务,那还剩下1个任务就交给非核心线程处理(增设了一个额外的线程)
        // 8个任务:三个核心线程处理其中3个任务,等待队列存放3个任务,那还剩下2个任务就交给两个非核心线程处理(增设了两个额外的线程)
        // 9个任务:三个核心线程处理其中3个任务,等待队列存放3个任务,那还剩下3个任务,但是最大线程数是5个,
        // 也就是说再多只能在三个核心线程的基础上再增设两个额外的线程,但是也不够处理剩下的3个任务
        // 或者说 最大线程数大小 5 + 等待队列大小 3 = 8 < 任务所需的线程数 9
        for (int i = 1; i <= 3; i++) {
            int taskId = i;
            Future<Integer> future = executorService1.submit(new Callable() {
                @Override
                public Integer call() {
                    System.out.println(Thread.currentThread().getName() + "正在处理业务" + taskId);
                    return taskId;
                }
            });
            futureList.add(future);
        }

        Iterator<Future<Integer>> it = futureList.iterator();
        while (it.hasNext()) {
            Future<Integer> future = it.next();
            try {
                Integer result = future.get();
                System.out.println("Result: " + result);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } catch (ExecutionException e) {
                throw new RuntimeException(e);
            }
        }

        // 使用execute方式提交任务
        for (int i = 1; i <= 9; i++) {
            int taskId = i;
            executorService1.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "正在处理业务" + taskId);
                }
            });
        }
    }
}

标签:execute,柜台,优缺点,submit,任务,线程,new,ThreadPoolExecutor
From: https://blog.csdn.net/qq_45732909/article/details/140199054

相关文章

  • 【Unity几种数据存储之间的区别】PlayerPrefs、Json、XML、二进制、SQLite数据存储之
    ......
  • 微服务架构是什么?他有什么优缺点。其实的安全问题如何解决?
    微服务是指把一个应用程序划分多个独立的服务单元。服务单元之间使用轻量级的通信机制进行通信。优点:1,技术栈比较灵活,2.易于维护和升级3,易于扩展;针对需要的服务单元进行扩展而不必对整个程序进行重构缺点:1,部署难度大,运维复杂2.安全性问题,服务单元通信可能导致数据的泄露......
  • Python多线程-线程池ThreadPoolExecutor
    1.线程池不是线程数量越多,程序的执行效率就越快。线程也是一个对象,是需要占用资源的,线程数量过多的话肯定会消耗过多的资源,同时线程间的上下文切换也是一笔不小的开销,所以有时候开辟过多的线程不但不会提高程序的执行效率,反而会适得其反使程序变慢,得不偿失。为了防止无尽的线程......
  • 揭秘无人直播的优缺点 让科技探索秘密
    无人直播,犹如未来科技的璀璨明珠,悄然改变着直播领域的格局。这种在直播过程中无需真人主播参与的特殊形式,通过精密的自动化程序和智能机器人,实现了直播内容的展示与互动,其魅力无法抗拒。首先,无人直播的魅力在于其高效便捷。摆脱了真人主播的束缚,它实现了真正意义上的24小时不......
  • 动态线程池的设置实践
    引言目的:提升系统吞吐率和响应性,同时面临线程池参数配置难题。问题:参数配置不合理可能导致服务器负载过高、服务不可用或内存溢出等问题。解决方案:动态线程池,允许实时变更核心参数并监控运行状态。动态线程池定义概念:能够动态变更核心参数,监控并告警线程池状态的线程池。......
  • Windows编程之多线程事件对象(Event Object)用法详解
    目录一、前言二、基础用法三、API详解1.创建事件对象2控制事件状态3.等待事件对象:四、实战案例1.案例描述 2.代码设计 3.总设计代码4.运行结果一、前言        事件对象(EventObject)是我们在大型项目中,进行多线程同步处理的时候经常用到的一种内核对象......
  • 18. JAVA 多线程锁介绍
    1.前言本节内容主要是对Java多线程锁进行介绍,是对锁的一个全方位的概述,为我们对后续深入学习不同的锁的使用方法奠定一个良好的基础。本节内容的知识点如下:乐观锁与悲观锁的概念,以及两种锁之间的区别,这是并发编程中经常涉及到的知识点,这是本节课程的核心知识点,是热度很高......
  • Java_多线程:线程池
    1、线程池优点:降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行。提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一......
  • 【Linux】多线程(互斥 && 同步)
    我们在上一节多线程提到没有任何保护措施的抢票是会造成数据不一致的问题的。那我们怎么办?答案就是进行加锁。目录加锁:认识锁和接口:初始化:加锁&&解锁:全局的方式:局部的方式:原理角度理解:实现角度理解:同步:加锁:认识锁和接口:初始化:这个就是我们互斥锁的类型。......
  • 多线程笔记
    目录1.概念进程与线程的区别:多线程的概念 多线程优缺点​编辑创建多线程的几种方式1、继承Thread类2、实现Runnable接口3、实现Callable接口实现Runnable和Callable有什么区别:相同点:不同点:注意点:4、线程池实现多线程创建线程的方式的比较实现runnable和继承t......