JUC--线程池

news2025/1/11 18:50:01

目录

一、线程池的介绍

二、线程池的创建

 三、特殊线程池

3.1.CompletionService异步处理

3.2.ThreadPoolExecutor

 3.3 ForkJoinPool


        虽然多线程的技术大大帮助了程序运行的效率,但是在太多的线程的创建与销毁下,系统的开销也将会是非常庞大的。所以为了实现线程的可管理性,并且降低开销。则JUC中提供了线程池的概念,以及相关实现方法

一、线程池的介绍

面试题:线程池的实现

创建一个阻塞队列来容纳任务,在第一次执行任务时创建足够多的线程,并处理任务,之后每个工作线程自动从任务队列中获取线程,直到任务队列中任务为0为止,此时线程处于等待状态,一旦有工作任务加入任务队列中,即刻唤醒工作线程进行处理,实现线程的可复用性。

创建四种线程池的方法

方法名描述
newCachedThreadPool()创建一个可以根据需要自动调整线程数量的线程池。
newFixedThreadPool(int nThreads)创建具有固定数量线程的线程池。
newSingleThreadScheduledExecutor()创建仅包含单个线程的线程池,可以按照计划安排任务的执行。
newScheduledThreadPool(int corePoolSize)创建具有固定核心线程数量的线程池,可以按照计划安排任务的执行,并且具有一定数量的可变大小线程池。

四种线程池的作用

  • newCachedThreadPool():创建一个可以根据需要自动调整线程数量的线程池。适用于执行大量短期异步任务的场景,线程池会根据任务的数量自动增加或减少线程的数量。

  • newFixedThreadPool(int nThreads):创建具有固定数量线程的线程池。适用于需要控制并发线程数的场景,线程数量固定不变。

  • newSingleThreadScheduledExecutor():创建仅包含单个线程的线程池,可以按照计划安排任务的执行。适用于需要按顺序执行任务的场景,每个任务都会在前一个任务完成后再执行。

  • newScheduledThreadPool(int corePoolSize):创建具有固定核心线程数量的线程池,可以按照计划安排任务的执行,并且具有一定数量的可变大小线程池。适用于需要根据计划安排任务执行时间的场景,核心线程数固定,但可以根据需要增加额外的线程来处理更多任务。

创建线程池

Executor类创建的线程池主要通过两类接口描述:ExecutorService(线程池)和ScheduledExecutorService(调度线程池)

线程池常用的方法

方法名描述
execute(Runnable command)按顺序执行给定的命令,提交到线程池中进行执行。
submit(Runnable task)提交一个可运行的任务给线程池,并返回一个表示该任务的未决结果的 Future。
submit(Callable task)提交一个可调用的任务给线程池,并返回一个表示该任务的未决结果的 Future。
invokeAll(Collection<? extends Callable<T>> tasks)在给定的任务列表中的所有任务完成之前,按顺序执行并等待每个任务的完成,并返回一个表示所有任务结果的 Future 列表。
invokeAny(Collection<? extends Callable<T>> tasks)执行给定的任务列表,返回其中一个已经成功完成的任务的结果,并取消所有其他任务。
isShutdown()如果线程池已经调用了 shutdown()shutdownNow() 方法,返回 true。否则返回 false
shutdown()平滑地关闭线程池,停止接受新任务,并尝试将所有未完成的任务继续执行。
schedule()安排在给定的延迟后执行任务,并返回一个表示该任务的未决结果的 ScheduledFuture

二、线程池的创建

面试题:线程池中的线程是怎么创建的?

四种线程对象的创建

1.案例代码:创建缓存线程池(建立了一个线程池,而且线程数量是没有限制的(当然,不能超过Integer的最大值),新增一个任务即有一个线程处理,或者复用之前空闲的线程,或者重亲启动一个线程,但是一旦一个线程在60秒内一直处于等待状态时(也就是一分钟无事可做),则会被终止)

package Example2135;


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

public class javaDemo {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
//        如果线程池中的线程不够用,则会自动新创建线程,线程最多为Integer.MAX_VALUE个
        for (int i=0;i<100;i++){
            executorService.submit(()->{
                System.out.println(Thread.currentThread().getId()+"--"+Thread.currentThread().getName());
            });
        }
        if (!executorService.isShutdown()){
            executorService.shutdown();
        }
    }
}

注意:关于execute与submit方法的关系

  1. 返回值类型:execute() 方法没有返回值,仅用于提交可运行的任务;而 submit() 方法将提交的任务封装成一个 Future 对象,可以通过该对象获取任务执行的结果。

  2. 参数类型:execute() 方法接受一个 Runnable 类型的参数,用于提交不需要返回结果的任务;而 submit() 方法可以接受 Runnable 或者 Callable 类型的参数,用于提交既可以返回结果也可以不返回结果的任务。

  3. 异常处理:execute() 方法无法捕获任务执行过程中的异常,如果任务抛出异常,线程池无法感知;而 submit() 方法可以捕获任务执行过程中的异常,并以 Future 形式返回异常信息。

综上所述,execute() 更适合用于执行简单的、无需返回结果的任务,而 submit() 则更灵活,可以用来执行有返回结果的任务,并且可以捕获任务的异常。

 2.案例代码:创建固定长度的线程池(在初始化时已经决定了线程的最大数量,若任务添加的能力超出了线程的处理能力,则建立阻塞队列容纳多余的任务)

package Example2136;

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

public class javaDemo {
    public static void main(String[] args) {
//    创建固定长度为4的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(4);
//           让线程输出自己的默认名称
        for(int i=0;i<10;i++){
            executorService.execute(()->{
                System.out.println(Thread.currentThread().getName());
            });
        }
        executorService.shutdown();
    }

}

3.案例代码:创建单线程(顾名思义就是一个池中只有一个线程在运行,该线程永不超时,而且由于是一个线程,当有多个任务需要处理时,会将它们放置到一个无界阻塞队列中逐个处理)

package Example2137;

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

public class javaDemo {
    public static void main(String[] args) {
//        创建单线程对象
        ExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
        for (int i=0;i<10;i++){
            executorService.submit(()->{
                System.out.println(Thread.currentThread().getName());
            });
        }
        executorService.shutdown();
    }
}

 4.案例代码:创建调度线程池

package Example2139;

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class javaDemo {
    public static void main(String[] args) {
//        创建调度线程池,并设置内核线程数量为1
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
//        设置调度的任务,并设置3秒后执行,之后每隔2秒执行一次
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                Date date = new Date();
                System.out.println(Thread.currentThread().getName()+"为您播报:当前时间为"+ date);
            }
        },3,2, TimeUnit.SECONDS);
    }
}

如果在线程池中传入了Callable接口实例,那么可以通过Future接口获取返回的结果在ExecutorService接口中提供了invokeAny和invokeAll两个方法可以实现一组Callable实例的执行

案例代码:执行一组Callable实例

package Example2140;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.*;

public class javDemo {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
//        保存多个线程对象
        Set<Callable<String>> allThread = new HashSet<Callable<String>>();
//        集合中追加线程
        for (int i=0;i<10;i++){
            final int temp = i;
            allThread.add(()->{
               return Thread.currentThread().getName()+temp;
            });
        }
//        创建固定长度的线程
        ExecutorService service = Executors.newFixedThreadPool(3);
//        使用Future接收类型
        List<Future<String>> list = service.invokeAll(allThread);
        for (Future<String> future : list){
            System.out.println(future.get());
        }
    }
}


 三、特殊线程池

3.1.CompletionService异步处理

CompletionService的主要功能是可以通过异步处理获取到线程池返回的结果,CompletionService可以接收Callable或者Runnable实现的线程任务。并且可以通过ExecutorCompletionService子类实例化接口对象

什么是异步处理

异步处理指的是在任务执行期间,不需要等待任务完成就可以继续执行其他操作。通过异步处理,可以提高程序的响应速度和效率。在传统的同步处理中,必须等待一个任务执行完毕后才能执行下一个任务,而异步处理则可以同时执行多个任务,提高了任务的并发性。

案例:使用CompletionService接口获取异步执行任务结果

package Example2141;

import java.util.concurrent.*;

//线程体
class ThreadItem implements Callable<String> {
    @Override
    public String call() throws Exception {
        //        获取当前时间戳
        long TimeMillis = System.currentTimeMillis();
        try {
            System.out.println("[start]"+Thread.currentThread().getName());
            TimeUnit.SECONDS.sleep(3);
            System.out.println("[end]"+Thread.currentThread().getName());
        }catch (Exception e){}
        return Thread.currentThread().getName()+":"+ TimeMillis;
    }
}

public class javaDemo {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        CompletionService<String> completionService = new ExecutorCompletionService<String>(executorService);
//        信息生产者
        for (int i=0;i<10;i++){
//            提交线程
            completionService.submit(new ThreadItem());
        }
        for (int i=0;i<10;i++){
            System.out.println("获取数据"+completionService.take().get());
        }
//        关闭线程池
        executorService.shutdown();
    }
}

3.2.ThreadPoolExecutor

通过Executors类可以进行实现线程池的创建,而通过Executors类的创建都是基于ThreadPoolExecutor类的实现创建。在一些特殊的环境下,开发者也可以直接使用ThreadPoolExecutor类结合阻塞队列与拒绝策略实现属于自己的线程池。

什么是拒绝策略

拒绝策略(Rejection Policy)是在线程池中,当提交的任务超过线程池容量且无法处理时,决定如何处理这些任务的一种策略。当线程池中的工作队列(阻塞队列)已满,并且没有空闲的线程可以执行任务时,就会触发拒绝策略。

四种拒绝策略

拒绝策略描述
AbortPolicy默认策略,当线程池无法处理新任务时,直接抛出RejectedExecutionException异常,表示拒绝执行该任务。
CallerRunsPolicy当线程池无法处理新任务时,将任务返回给调用者进行处理。也就是由调用submit方法的线程来执行该任务。
DiscardOldestPolicy当线程池无法处理新任务时,会丢弃最早进入工作队列的任务,并尝试重新提交当前任务。
DiscardPolicy当线程池无法处理新任务时,会默默丢弃无法处理的任务,不给予任何提示。该策略可能会导致任务丢失,慎用。

案例代码:

package Example2142;

import java.util.concurrent.*;

public class javaDemo {
    public static void main(String[] args) {
        BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(2);
//        通过ThreadPoolExecutor创建线程池,该线程有2个内核线程,最大线程量为2,每个线程存活时间为6秒,使用默认拒绝策略
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,2,6L, TimeUnit.SECONDS,queue, Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
        for (int i=0;i<5;i++){
            threadPoolExecutor.submit(()->{
                System.out.println("[BEFORE]"+Thread.currentThread().getName());
                try {
                    TimeUnit.SECONDS.sleep(2);
                }catch (Exception  e){}
                System.out.println("[AFTER]" +Thread.currentThread().getName());
            });
        }
        
    }
}

 3.3 ForkJoinPool

在JDK1.7以后为了充分利用多核CPU的性能优势,可以将一个复杂的业务计算进行拆分,交由多台CPU并行计算,这样用以提高程序的执行性能,所以引入了ForkJoinPool类,该类包含两个操作

Fork(分解操作):将一个大型业务拆分成多个小的任务放在框架中执行

Join(合并操作):主任务将等待多个子任务完成后进行合并

在ForkJoinPool中有两个子类,RecursiveTask(有返回值的任务)、RecursiveAction(无返回值的任务)

该类常用的方法:

方法说明
fork()将任务分解为更小的子任务并异步执行。将当前任务放入工作队列中,以供其他工作线程获取并执行。
join()等待当前任务的执行结果,并返回结果。如果任务还没有完成,则调用线程会被阻塞,直到任务完成后才会继续执行。
isCompleteNormally()判断任务是否已经正常完成。如果任务在完成时没有抛出异常,该方法返回true;如果任务被取消或者发生异常,返回false
invokeAll(tasks)执行给定的任务集合,并等待所有任务完成。该方法会将任务集合拆分为更小的子任务,并由线程池中的工作线程异步执行。
getException()获取任务执行过程中所发生的异常。如果任务正常完成或者尚未完成,该方法返回null;如果任务被取消或者异常中止,返回引发异常的原因。

案例代码:

package Example2143;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;

class SumTask extends RecursiveTask<Integer> {
    private int start;
    private int end;

    public SumTask(int start, int end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Integer compute() {
        int sum = 0;
        if (this.end - this.start < 100) {
            for (int x = this.start; x <= this.end; x++) {
                sum += x;
            }
        } else {
            int middle = (start + end) / 2;
            SumTask leftTask = new SumTask(this.start, middle);
            SumTask rightTask = new SumTask(middle + 1, this.end);
            leftTask.fork();
            rightTask.fork();
            // 等待子任务完成并将结果相加
            sum = leftTask.join() + rightTask.join();
        }
        return sum;
    }
}

public class javaDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        SumTask task = new SumTask(0,100);
        ForkJoinPool pool = new ForkJoinPool();
        Future<Integer> future = pool.submit(task);
        System.out.println(future.get());
    }
}


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

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

相关文章

ChatGPT赋能低代码开发:打造智能应用的双重引擎

摘要&#xff1a;本文摘自葡萄城低代码产品活字格的资深用户&#xff08;格友超哥&#xff09;所撰写的文章&#xff1a;《惊叹表现&#xff01;活字格ChatGPT&#xff1a;低代码开发智能应用的巨大潜力》。 ChatGPT的functions函数使用方 自从OPENAI发布了最新的GPT引擎gpt-3…

由银行保险业科技外包供应商安全风险事件,看金融行业供应链安全

近日&#xff0c;金融监管总局下发《关于加强第三方合作中网络和数据安全管理的通知》&#xff0c;通知中提到&#xff1a;近期发生多起银行保险外包供应商安全⻛险事件&#xff0c;对银行保险机构的网络系统安全、业务连续性造成一定影响&#xff0c;暴露出银行保险机构在外包…

春秋云镜:CVE-2018-19422(Subrion CMS 4.2.1 存在文件上传漏洞)

一、题目 靶标介绍&#xff1a; Subrion CMS 4.2.1 存在文件上传漏洞 进入题目&#xff1a; admin/admin 点击设置&#xff1a; 后台管理主页&#xff1a; 上传页面&#xff1a; 上传目录&#xff1a; /panel/uploads/ 查看上传位置&#xff1a; system.pht<?php sys…

Lua代码实现鼠标宏

注意&#xff1a;本文仅是技术交流&#xff0c;滥用技术者将自行承担后果 目录 一、什么是鼠标宏 二、射击游戏鼠标宏的制作原理 三、FPX鼠标宏带来的危害 一、什么是鼠标宏 鼠标宏是一种使用特定软件或设备编写和执行的自动化脚本&#xff0c;用于模拟和复制鼠标操作。它可…

PowerDesigner书签显示comment字段注释内容

1.今日书签 PowerDesigner 16.5 对表字段设置显示 comment 注释&#xff0c;解决视图模型看不到表字段 comment 中文注释的问题。 2.拨云见日 2.1.操作环境 操作系统&#xff1a;Win10 PowerDesigner 版本&#xff1a;16.5 2.2.操作步骤 无论 PowerDesigner 15 还是 Powe…

Vue 中 diff 算法原理

一、Diff 概念 Vue 基于虚拟 DOM 做更新。diff 算法的核心就是比较两个虚拟节点的差异。 Vue 的 diff 算法是平级比较&#xff08;如图&#xff0c;父级和父级比较&#xff0c;儿子和儿子比较&#xff0c;孙子和孙子比较&#xff09;&#xff0c;不考虑跨级比较的情况&#x…

企业专网?公网?工业4G路由器如何正确选择SIM卡?

选择合适的SIM卡对工业4G路由器稳定通信至关重要。但是面对企业专网和公网两种选择,用户该如何抉择呢?本文将全面解析专网卡与公网卡的区别、适用场景及选择要点,并推荐星创易联SR700等产品的匹配方案,助你正确选择最合理的工业SIM卡。 首先,从网络质量上,企业专网较公网更稳定…

韩语翻译器拍照翻译方法,几个步骤轻松翻译

韩语是一种非常有趣的语言&#xff0c;但对于大多数人来说&#xff0c;是非常难以掌握的。 如果你正在韩国旅行或生活&#xff0c;你可能会遇到许多韩语文字和标志&#xff0c;这时候拍照翻译就派上用场了。 许多智能手机都有支持翻译应用&#xff0c;但是如何在手机上拍照并翻…

Java版B/S架构 智慧工地源码,PC、移动、数据可视化智慧大屏端源码

智慧工地是什么&#xff1f;智慧工地主要围绕绿色施工、安全管控、劳务管理、智能管理、集成总控等方面&#xff0c;帮助工地解决运营、管理方面各个难点痛点。在互联网的加持下促进项目现场管理的创新与发展&#xff0c;实现工程管理人员与工程施工现场的整合&#xff0c;构建…

一网打尽java注解-克隆-面向对象设计原则-设计模式

文章目录 注解内置注解元注解 对象克隆为什么要克隆&#xff1f;如何克隆浅克隆深克隆 Java设计模式什么是设计模式&#xff1f;为什么要学习设计模式&#xff1f; 建模语言类接口类之间的关系依赖关系关联关系聚合关系组合关系继承关系实现关系 面向对象设计原则单一职责开闭原…

速通蓝桥杯嵌入式省一教程:(九)AT24C02芯片(E2PROM存储器)读写操作与I2C协议

AT24C02芯片&#xff08;又叫E2PROM存储器、EEPROM存储器&#xff09;&#xff0c;是一种通过I2C(IIC)协议通信的掉电保存存储器芯片&#xff0c;其内部含有256个8位字节。在介绍这款芯片之前&#xff0c;我们先来粗略了解一下I2C协议。 I2C总线是一种双向二线制的同步串行总线…

BLFS学习系列 第26章. 显示管理器 —— 总述

显示管理器&#xff08;Display Manager&#xff09;是用于启动图形显示&#xff08;当前为X服务器&#xff09;并为窗口管理器或桌面环境提供登录功能的图形程序。 有许多显示管理器可用。一些较为知名的包括&#xff1a;GDM、KDM&#xff08;已弃用&#xff09;、LightDM、L…

ssm助学贷款系统源码和论文

ssm助学贷款系统源码和论文050 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&am…

Java注解语法

Java注解语法 1. 前置基础 ​ 学习java反射语法 JAVA通过反射使用公共构造方法和私有构造方法来创建对象 2. Java注解是什么&#xff1f; ​ Java注解是代码中的特殊标记&#xff0c;比如Override、Test等&#xff0c;作用是&#xff1a;让其他程序根据注解 信息决定怎么执…

【ECCV2022】Swin-Unet: Unet-like Pure Transformer for Medical Image Segmentation

Swin-Unet: Unet-like Pure Transformer for Medical Image Segmentation 论文&#xff1a;https://arxiv.org/abs/2105.05537 代码&#xff1a;https://github.com/HuCaoFighting/Swin-Unet 解读&#xff1a;Swin-UNet&#xff1a;基于纯 Transformer 结构的语义分割网络 -…

【FAQ】视频云存储/安防监控EasyCVR视频汇聚平台如何通过角色权限自行分配功能模块?

视频云存储/安防监控EasyCVR视频汇聚平台基于云边端智能协同&#xff0c;支持海量视频的轻量化接入与汇聚、转码与处理、全网智能分发、视频集中存储等。音视频流媒体视频平台EasyCVR拓展性强&#xff0c;视频能力丰富&#xff0c;具体可实现视频监控直播、视频轮播、视频录像、…

SpringMVC 写个 HelloWorld

文章目录 一、SpringMVC简介1、什么是MVC2、什么是SpringMVC3、SpringMVC的特点 二、HelloWorld1、开发环境2、创建maven工程a>添加web模块b>打包方式&#xff1a;warc>引入依赖 3、配置web.xmla>默认配置方式b>扩展配置方式 4、创建请求控制器5、创建springMVC…

Linux下彻底卸载jenkins

文章目录 1、停服务进程2、查找安装目录3、删掉相关目录4、确认已完全删除 1、停服务进程 查看jenkins服务是否在运行&#xff0c;如果在运行&#xff0c;停掉 ps -ef|grep jenkins kill -9 XXX2、查找安装目录 find / -name "jenkins*"3、删掉相关目录 # 删掉相…

继承(C++)

继承 一、初识继承概念“登场”语法格式 继承方式九种继承方式组合小结&#xff08;对九种组合解释&#xff09; 二、继承的特性赋值转换 一一 切片 / 切割作用域 一一 隐藏 / 重定义 三、派生类的默认成员函数派生类的默认成员函数1. 构造函数2. 拷贝构造3. 赋值运算符重载4. …

部署FTP服务(二)

目录 2.访问FTP服务 1.使用ftp命令行工具 2.使用浏览器 3.使用FileZilla Client 3.Serv-U 1.定义新域 2.创建用户 4. windowsserver搭建ftp服务器 一、FTP工具 二、Windows资源管理器 三、IE浏览器访问 2.访问FTP服务 下面在一台装有Windows10操作系统的计算机中&#…