Java中常见的线程池

news2024/11/18 21:25:46

一、Java中常见的线程池

1.为什么使用线程池

  • 重用线程池的线程,避免因为线程的创造和销毁所带来的性能开销。
  • 有效控制线程池的最大并发数,避免大量的线程之间因抢占系统资源而阻塞。
  • 能够对线程进行简单的管理,并提供一些特定的操作,如:定时、定期、单线程、并发数控制等功能。

2.线程池可能带来的风险

  • 死锁
    任何多线程应用程序都有死锁风险。当一组进程或线程中的每一个都在等待一个只有该组中另一个进程才能引起的事件时,我们就说这组进程或线程死锁了。
  • 资源不足
    一般都是由于线程池设置过大引起的。
  • 并发错误
    线程池和其他排队机制依靠使用wait()和notify()方法,这两个方法都难于使用。如果编码不正确,那么可能会丢失通知,导致线程保持空闲状态,尽管队列中还有任务正在运行。
  • 线程泄露
    任务抛出一个RuntimeException或Error时,如果池类没有捕捉到他们,那么线程将会退出而线程池的大小将会永久减少一个,当这种情况的次数发生的足够多时,线程池将会为空,系统将停止运行,因为没有可用的线程来处理任务。
  • 请求过载
    线程池任务队列中,待执行的任务积累过多,消耗太多的系统资源并引起资源缺乏。

二、Executor

Java5之后,并发编程引入了一堆启动、管理和调度线程的APIExecutor 框架
便是Java 5中引入的,其内部使用了线程池机制,它在java.util.cocurrent包下,通过
该框架来控制线程的启动、执行和关闭,可以简化并发编程的操作。因此,在Java 5
后,通过 Executor 来启动线程比使用 Threadstart 方法更好,除了更易管理,效率更
好(用线程池实现,节约开销)外,还有关键的一点:有助于避免this逃逸问题——如果
我们在构造器中启动一个线程,因为另一个任务可能会在构造器结束之前开始执行,此时
可能会访问到初始化了一半的对象用Executor在构造器中。Eexecutor作为灵活且强大的
异步执行框架,其支持多种不同类型的任务执行策略,提供了一种标准的方法将任务的提
交过程和执行过程解耦开发,基于生产者-消费者模式,其提交任务的线程相当于生产者,
执行任务的线程相当于消费者,并用Runnable来表示任务,Executor的实现还提供了对生
命周期的支持,以及统计信息收集,应用程序管理机制和性能监视等机制。
在这里插入图片描述
Executor框架包括:线程池、Executor,Executors,ExecutorService,CompletionService,Futurn,Callable等。

1.Executor和ExecutorService

Executor:一个接口,其定义了一个接收Runnable对象的方法executor,其方法签名
executor(Runnable command) ,该方法接收一个 Runable 实例,它用来执行一个任务,任务即一个实现了Runnable接口的类,一般来说,Runnable任务开辟在新线程中的使用方法为:new Thread(new RunnableTask()).start() ,但在 Executor 中,可以使用Executor而不用显示地创建线程:executor.execute(new RunnableTask()),Executor接口并不严格要求执行是异步的。
ExecutorService:是一个比Executor使用更广泛的子类接口,其提供了生命周期管理的方法,返回 Future 对象,以及可跟踪一个或多个异步任务执行状况返回Future的方法;可以调用 ExecutorService shutdown() 方法来平滑地关闭 ExecutorService ,调用该方法后,将导致ExecutorService停止接受任何新的任务且等待已经提交的任务执行完成(已经提交的任务会分两类:一类是已经在执行的,另一类是还没有开始执行的),当所有已经提交的任务执行完毕后将会关闭 ExecutorService `。因此我们一般用该接口来实现和管理多线程。

三、Executors

Executors类,是一个工具、工厂类,提供了一系列工厂方法用于创建线程池,返回的线程池都实现了 ExecutorService 接口。常见的线程池都可以通过此类创建。

1.CachedThreadPool

可缓存线程池。一种线程数量不定的线程池,并且其最大线程数为 Integer.MAX_VALUE
这个数是很大的,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则
新建线程。这些池通常会提高执行许多短暂异步任务的程序的性能。调用execute将重用
以前构造的线程(如果可用)。如果没有可用的线程,将创建一个新的线程并将其添加到
该池中。但是线程池中的空闲线程都有超时限制,这个超时时长是60秒,超过60秒闲置线
程将被终止并从缓存中删除。因此,长时间保持闲置的池将不会消耗任何资源。
创建方法:

public static ExecutorService newCachedThreadPool();
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory);
package thread.day2;

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

public class CachedThreadPoolExample {
    public static void main(String[] args) {
        //可缓存线程池,空闲线程60s后回收
        ExecutorService pool = Executors.newCachedThreadPool();
        System.out.println(LocalTime.now());
        for (int i = 0; i < 10; i++) {
            int num=i;
            pool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("当前时间:"+LocalTime.now()+",线程:"+Thread.currentThread().getName()
                    +":"+num);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
        }
    }
}

2.FixedThreadPool

创建一个可重用固定个数的线程池,当线程处于空闲状态时,他们并不会被回收,除非线程池被关闭了,如果工作线程数量达到线程池初始的最大容量,则将提交的任务存入池队列中。由于 newFixedThreadPool 只有核心线程并且这些核心线程不会被回收,这样它更加快速的响应外界的请求。
创建方式:

public static ExecutorService newFixedThreadPool(int nThreads);
public static ExecutorService newFixedThreadPool(int nThreads,
ThreadFactory threadFactory);
package thread.day2;

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

public class FixedThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(2);
        System.out.println("当前时间:"+LocalTime.now());

        for (int i = 0; i < 10; i++) {
            int num=i;
            pool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("当前时间:"+LocalTime.now()+",线程:"+Thread.currentThread().getName()+":"+num);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
        }
    }
}

3.ScheduledThreadPool

创建一个定长线程池,支持定时及周期性任务执行。它的线程数量是固定的,它可安排
给定延迟后运行命令或者定期地执行,这类线程池主要用于执行定时任务和具有固定周期
的重复任务。
创建方式:

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize);
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize,
ThreadFactory
threadFactory)
package thread.day2;

import java.time.LocalTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Demo08 {
    public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
        System.out.println("当前时间:"+ LocalTime.now());
        //周期执行
//        scheduledExecutorService.scheduleAtFixedRate(()->{
//                System.out.println("当前时间:"+LocalTime.now()+",线程:"+Thread.currentThread().getName());
//            try {
//                Thread.sleep(3000);
//            } catch (InterruptedException e) {
//                throw new RuntimeException(e);
//            }
//
//        },2,5, TimeUnit.SECONDS);
        scheduledExecutorService.scheduleWithFixedDelay(() -> {
            System.out.println("当前时间:" + LocalTime.now() + ", 线程: " + Thread.currentThread().getName() + ", 序号:");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, 0, 2, TimeUnit.SECONDS);

        for (int i = 0; i < 10; i++) {
            int num=i;
            //正常线程池执行任务
//            scheduledExecutorService.execute(()->{
//                System.out.println("当前时间:"+LocalTime.now()+",线程:"+Thread.currentThread().getName()+":"+num);
//                try {
//                    Thread.sleep(3000);
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }
//            });

            //定时执行
//            scheduledExecutorService.schedule(()->{
//                System.out.println("当前时间:"+LocalTime.now()+",线程:"+Thread.currentThread().getName()+":"+num);
//                try {
//                    Thread.sleep(3000);
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }
//
//            },2, TimeUnit.SECONDS);
        }
    }
}

区别:

scheduleAtFixedRate方法,如果要执行的任务耗时比任务的间隔时间长,下次任务执行的时间就是上次任务执行完就执行。(排队)
scheduleWithFixedDelay方法,下次任务的执行时间就是上次任务执行所需的时间加上时间间隔。

4.SingleThreadExecutor

创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证任务按照指定顺序(FIFO,LIFO)执行。

public static ExecutorService newSingleThreadExecutor();
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)
package thread.day2;

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

/**
 * 使用单线程的线程池可以保证任务按照提交的顺序依次执行,每次只有一个任务在执行。
 */
public class Demo07 {
    public static void main(String[] args) {
        //创建一个单线程的线程池
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            int num=i;
            executorService.execute(()->{
                System.out.println("当前时间:"+ LocalTime.now()+",线程:"+Thread.currentThread().getName()+":"+num);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });

        }
        executorService.shutdown();
    }
}

四、Executor VS ExecutorService VS Executors

这三者均是Executor框架中的一部分。总结一下这三者间的区别:

  • ExecutorService接口继承了Executor接口,是Executor的子接口。
  • Executor接口定义了一个execute()方法用来接收一个Runnable接口的对象,而ExecutorService接口中的submit()方法可以接收RunnableCallable接口的对象。
  • Executor接口中的execute()方法不返回任何结果,而ExecuteService接口中的submit()方法可以通过一个Future对象返回运行结果。
  • 除了允许客户端提交一个任务, ExecutorService 还提供用来控制线程池的方法。比如:调用 shutDown() 方法终止线程池。
  • Executors类提供工厂方法用来创建不同类型的线程池。比如: newSingleThreadExecutor() 创建一个只有一个线程的线程池, newFixedThreadPool(int numOfThreads) 来创建固定线程数的线程池, newCachedThreadPool() 可以根据需要创建新的线程,但如果已有线程是空闲的会重用已有线程。

阿里开发规范中强制要求禁用Executors创建线程池。
【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors返回的线程池的弊端如下:
1) FixedThreadPool 和 SingleThreadPool :
允许的请求队列的长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
2) CachedThreadPool 和 ScheduledThreadPool :
允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
在这里插入图片描述
判断核心线程数是否已满,核心线程数大小和corePoolSize参数有关,未满则创建线程执行任务,若核心线程池已满,判断队列是否满,队列是否满和workQueue参数有关,若未满则加入队列中,若队列已满,判断线程池是否已满,线程池是否已满和maximumPoolSize参数有关,若未满创建线程执行任务,若线程池已满,则采用拒绝策略处理无法执执行的任务,拒绝策略和handler参数有关

五、ThreadPoolExecutor

使用 ThreadPoolExecutor 可以创建出符合自己的业务场景需要的线程池。

public class ThreadPoolExecutor extends AbstractExecutorService

1.构造方法

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,
TimeUnit unit,BlockingQueue<Runnable> workQueue);
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,
TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory);
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,
TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler);
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,
TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,
RejectedExecutionHandler handler)

实际上最后都调用的是最后一个(参数最多的那个)构造方法:

  • corePoolSize:线程池中的核心线程数,当提交一个任务时,线程池创建一个新线程
    执行任务,直到当前线程数等于 corePoolSize ;如果当前线程数为 corePoolSize
    继续提交的任务被保存到阻塞队列中,等待被执行;如果执行了线程池的 prestartAll CoreThreads() 方法,线程池会提前创建并启动所有核心线程。当线程数小于等于 core PoolSize 时,默认情况下线程会一直存活在线程池中,即使线程处于空闲状态。如果
    allowCoreThreadTimeOut 被设置为 true 时,无论线程数多少,那么线程处于空闲状
    态超过一定时间就会被销毁掉。
  • maximumPoolSize:线程池中允许的最大线程数。如果当前阻塞队列满了,且继续提 交任务,则创建新的线程执行任务,前提是当前线程数小于 maximumPoolSize 。
  • keepAliveTime: 线程空闲时的存活时间,即当线程没有任务执行时,继续存活的时 间;默认情况下,该参数只在线程数大于corePoolSize 时才有用;如果 allowCoreThr eadTimeOut 被设置为 true时,无论线程数多少,线程处于空闲状态超过一定时间就会被销毁掉。
  • unit: keepAliveTime 的单位。 TimeUnit 是一个枚举类型,其包括:
    - NANOSECONDS :1微毫秒 = 1微秒 / 1000
    - MICROSECONDS :1微秒 = 1毫秒 / 1000
    - MILLISECONDS :1毫秒 = 1秒 /1000
    - SECONDS :秒
    - MINUTES :分
    - HOURS :小时
    - DAYS :天
  • workQueue: 用来保存等待被执行的任务的阻塞队列,且任务必须实现 Runable 接口,
    有如下阻塞队列:
    - ArrayBlockingQueue :基于数组结构的有界阻塞队列,按FIFO排序任务;
    - LinkedBlockingQuene :基于链表结构的无界阻塞队列,按FIFO排序任务,吞吐量
    通常要高于 ArrayBlockingQuene ;
    - SynchronousQuene :一个不存储元素的阻塞队列,每个插入操作必须等到另一个
    线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于 LinkedBl
    ockingQuene ;
  • threadFactory: 创建线程的工厂,通过自定义的线程工厂可以给每个新建的线程设置
    一个具有识别度的线程名
  • handler: 线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提
    交任务,必须采取一种策略处理该任务,线程池提供了4种策略:
    - AbortPolicy :直接抛出异常,默认策略;
    - CallerRunsPolicy :用调用者所在的线程来执行任务;
    - DiscardOldestPolicy :丢弃阻塞队列中靠最前的任务,并执行当前任务
    - DiscardPolicy :直接丢弃任务;

ScheduledThreadPool 线程池是使用 ScheduledThreadPoolExecutor 类创建的。
ScheduledThreadPoolExecutor 类是 ThreadPoolExecutor 的子类。

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

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

相关文章

PY32F003F18之DMA串口

PY32F003F18使用DMA串口&#xff0c;官方程序省FLASH&#xff0c;但不省内存。单片机内存够大&#xff0c;节省没意义&#xff0c;故做了修改&#xff0c;少用HAL库中的发送和接收&#xff0c;从里面抠出有用的部分&#xff0c;修修改改就可以了。 一、DMA串口初始化流程&…

未在本地计算机上注册“Microsoft.ACE.OLEDB.12.0”提供程序

1.第一步&#xff1a;下载 Microsoft Access Database Engine Microsoft Access Database Engine 2010 Redistributable Download 2.第二步: 安装下载的引擎软件 3.第三步&#xff1a;重新连接&#xff1b; 代码如下: private void button1_Click(object sender, EventArgs …

【M malloc送书第二期】朋友圈大佬都去读研了,这份备考书单我码住了!

文章目录 01 《**数据结构与算法分析**》02 《计算机网络&#xff1a;自顶向下方法》03 《现代操作系统》04 《深入理解计算机系统》01 《概率论基础教程&#xff08;原书第10版》03 《线性代数及其应用》 八九月的朋友圈刮起了一股晒通知书潮&#xff0c;频频有大佬晒出“研究…

从统计语言模型到预训练语言模型---统计语言模型

语言模型 从历史上来看&#xff0c; 自然语言处理的研究范式变化是从规则到统计&#xff0c; 从统计机器学习到基于神经网络的深度学习&#xff0c;这同时也是语言模型发展的历史。要了解语言模型的发展历史&#xff0c;首先我们需要认识什么是语言模型。语言模型的目标是建模…

RT-Thread I/O设备模型框架

I/O 设备模型框架 RT-Thread提供了一套简单的I/O设备模型框架&#xff0c;如图所示&#xff0c;它位于硬件和应用程序之间&#xff0c;共分成三层&#xff0c;从上到下分别是I/O设备管理层、设备驱动框架层、设备驱动层。 应用程序通过I/O设备管理接口获得正确的设备驱动&…

【AI视野·今日CV 计算机视觉论文速览 第249期】Tue, 19 Sep 202

AI视野今日CS.CV 计算机视觉论文速览 Tue, 19 Sep 2023 (showing first 100 of 152 entries) Totally 152 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computer Vision Papers GEDepth: Ground Embedding for Monocular Depth Estimation Authors Xiaodong Yang…

【vue3页面展示代码】展示代码codemirror插件

技术版本&#xff1a; vue 3.2.40、codemirror 6.0.1、less 4.1.3、vue-codemirror 6.1.1、 codemirror/lang-vue 0.1.2、codemirror/theme-one-dark 6.1.2 效果图&#xff1a; 1.安装插件 yarn add codemirror vue-codemirror codemirror/lang-vue codemirror/theme-one-dar…

ChatGpt介绍和国产ChatGpt对比

1.ChatGPT是美国OpenAI研发的聊天机器人程序&#xff0c;2022年11月30日发布。ChatGPT是人工智能技术驱动的自然语言处理工具&#xff0c;它能够通过理解和学习人类的语言来进行对话。 2.ChatGPT是一种基于自然语言处理的聊天机器人程序。它使用深度学习技术&#xff0c;通过对…

A+CLUB管理人支持计划第八期 | 量创投资

免责声明 本文内容仅对合格投资者开放&#xff01; 私募基金的合格投资者是指具备相应风险识别能力和风险承担能力&#xff0c;投资于单只私募基金的金额不低于100 万元且符合下列相关标准的单位和个人&#xff1a; &#xff08;一&#xff09;净资产不低于1000 万元的单位&…

抖 X-Bongus 参数逆向 python案例实战

前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 知识点&#xff1a; 动态数据抓包 requests发送请求 X-Bogus 参数逆向 开发环境: python 3.8 运行代码 pycharm 2022.3 辅助敲代码 requests pip ins…

python从入门到精通(一)

自己也有三四年的码龄了&#xff0c;目前&#xff0c;重拾起自己的博客&#xff0c;记录自己的学习笔记&#xff0c;为大家提供优质内容&#xff0c;也来巩固自己的学习内容。 很开心也成功成为了一名研究生&#xff0c;张张的研究方向是图像处理和计算机视觉这一块&#xff0c…

k8s手动下载镜像、通过容器创建镜像方法

手动下载镜像 1、首先pull镜像到本地 docker pull <镜像名称>:<标签>2、转储镜像 docker save -o /path/to/save/image.tar 3、解压 tar -xvf /path/to/save/image.tar补充 1、如果要将tar还原成镜像 docker load -i /path/to/save/image.tar或者用输入重定向…

Win10专业版开启远程桌面

Win10专业版开启远程桌面 方法一&#xff1a; 一、按“Win R”键&#xff0c;然后输入“sysdm.cpl”并按下回车键打开系统属性。 二、选择“远程”选项卡&#xff0c;在远程桌面中勾选“允许远程连接到此计算机”就可以开启远程桌面&#xff1b; 方法二&#xff1a; 一、打…

融云受邀参加 Web3.0 顶级峰会「Meta Era Summit 2023」

本周四 19:00-20:00&#xff0c;融云直播课 社交泛娱乐出海最短变现路径如何快速实现一款 1V1 视频应用&#xff1f; 欢迎点击上方小程序报名~ 9 月 12 日&#xff0c;由中国香港 Web3.0 媒体 Meta Era 主办的“Meta Era Summit 2023”在新加坡收官&#xff0c;融云作为战略合作…

Window11专业版安装Java环境

目录 一、首先准备好一个Java的环境包 二、在Windows11上双击运行此环境包 三、根据提示安装完成后&#xff0c;在此电脑上属性中搜索环境变量 四、配置环境变量、并验证 一、首先准备好一个Java的环境包 二、在Windows11上双击运行此环境包 按着要求进行下一步操作&#…

数据丢失防护工具

什么是数据丢失防护 数据丢失防护 &#xff08;DLP&#xff09; 涉及系统地识别、定位和评估具有内容和上下文感知的数据和用户活动&#xff0c;以应用策略或主动响应来防止数据丢失。静态、使用和动态数据必须受到持续监控&#xff0c;以发现企业数据存储、使用或共享方式的偏…

Docker-如何获取docker官网x86、ARM、AMD等不同架构下的镜像资源

文章目录 一、概要二、资源准备三、环境准备1、环境安装2、服务器设置代理3、注册docker账号4、配置docker源 四、查找资源1、服务器设置代理2、配置拉取账号3、查找对应的镜像4、查找不同版本镜像拉取 小结 一、概要 开发过程中经常会使用到一些开源的资源&#xff0c;比如经…

股票量化系统QTYX开启全自动实盘之路——第一弹easytrader库搭建本地自动交易环境...

搭建全自动化量化系统 股票量化交易系统QTYX最终形态是一款全自动化的量化交易系统。 接下来我们通过一个系列逐步把QTYX升级为全自动的系统。当前为第一篇&#xff1a;使用easytrader库搭建本地自动交易环境 股票的自动化交易接口在2015年之后就被管制了&#xff0c;于是对于普…

黑马JVM总结(十七)

&#xff08;1&#xff09;G1_简介 下面介绍一种Grabage one的垃圾回收器&#xff0c;在jdk9的时候称为默认的回收器&#xff0c;废除了之前的CMS垃圾回收器&#xff0c;它的内部也是并发的垃圾回收器 我们可以想到堆内存过大&#xff0c;肯定会导致回收速度变慢&#xff0c;因…

游戏开发玩法设计的重要性

玩法设计在游戏开发中非常重要&#xff0c;因为它直接影响着玩家的游戏体验和游戏的吸引力。一个精心设计的玩法可以使游戏更具趣味性、挑战性和可玩性&#xff0c;从而吸引更多的玩家并提高游戏的成功度。以下是玩法设计的重要性&#xff1a; 吸引力和沉浸感&#xff1a; 精心…