首页 > 编程语言 >Java 线程池

Java 线程池

时间:2024-01-18 23:11:09浏览次数:25  
标签:Java 队列 创建 任务 线程 new

Java 线程池

前言:
创建过多的线程会占用更多的内存、并且在线程切换的时候增加消耗的资源和浪费更多的时间,为了缓解以上问题,出现一种基于复用和预分配思想的技术,线程池。线程池中的线程被统一创建和管理,提高了系统响应时间和系统的资源利用率。
除了线程池解决以上问题外,在java21中引入了虚拟线程。在Go中,引入了协程来缓解创建过多线程导致性能的问题,但他们不是基于复用和预分配的思想(池化技术),而是自己实现了一套高效的任务调度机制。笔者在此主要介绍Java的线程池技术。
主要内容:

  1. java的默认现程池
  2. 自定义线程池的参数配置

JAVA的线程池配置

java创建一个线程池是通过以下代码:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

可以从传入的参数了解到创建一个线程池需要的参数有如下表:

参数 解释 作用 是否必需
corePoolSize 核心线程的数量 执行工作的线程,当任务到来,但线程数量小于核心现程数量,便会创建新的线程。线程数量大于核心线程,队列没满,把任务放入任务队列 必须
maximumPoolSize 最大线程数量 当任务到来,并且任务队列满且线程数少于最大线程数,便增加线程数量。 必需
keepAliveTime 空闲时间 当线程数量大于核心线程数量时,会销毁空闲了该时间的现程 必需
TimeUnit unit 时间单位 用于时间单位 必需
workQueue 任务队列 存放需要线程处理的任务 必需
threadFactory 线程工厂 创建基本线程的方式 非必需
RejectedExecutionHandler handler 拒接策略 当线程大于等于最大线程数且任务队列满,则执行拒绝策略 非必需

任务拒绝策略有以下选择:

拒绝策略 作用 使用场景
CallerRunsPolicy 当触发拒绝策略,只要线程池没有关闭的话,则使用调用线程直接运行任务。 对并发要求不高的场景,调用线程执行会影响当前线程执行的任务
AbortPolicy 丢弃任务,并抛出拒绝执行 RejectedExecutionException 异常信息 需要自己处理拒绝策略的情况下使用,比如Tomcat的线程池
DiscardPolicy 直接丢弃,其他啥都没有 任务不重要的情况下
DiscardOldestPolicy 当触发拒绝策略,只要线程池没有关闭的话,丢弃阻塞队列 workQueue 中最老的一个任务,并将新任务加入 任务一般重要,要求尽力处理的情况下,并且新任务比老任务重要

JAVA的默认线程池

FixedThreadPool

FixedThreadPool是固定长度线程池,由创建时自己设置线程池的线程数量。

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

从参数可以知道,这个Fixed线程池是个固定大小的线程池,并且任务队列是无界队列,没有拒绝策略,来多少任务处理多少任务。如果任务增加的速度大于现场池处理的速度,就容易造成内存泄露。

CachedThreadPool

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

从初始参数可以看出,没有核心线程大小,有最大线程大小,且最大线程为Inter的最大值,SynchronousQueue为任务队列。这个队列没有存储空间。意味着来多少任务,处理多少任务。这个线程池容易因创建大量线程而引起系统瘫痪,不适合处理花费时间长的任务,适合处理,花费时间小的任务。

SingleThreadExecutor

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

这个是单线程线程池,用于执行有先后顺序关联的任务。同样任务队列是无界的,需要注意内存溢出。

ScheduledThreadPool

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

该线程池主要用于执行定时任务或者延时任务。在java中还有个Timer类专门用于处理延时任务或者定时任务,但Timer中的任务会彼此影响执行的时间,导致时间的不准确。如果有多个延时任务或者定时任务可以使用ScheduledThreadPool 线程池来执行任务。并且一个任务出现异常也不会影响其它的任务执行。

引用:
Java中的线程池(4)----ScheduledThreadPool

Java默认提供的线程池

你知道线程池的 创建方式、7大参数、处理流程 和 最大线程数量该如何配置吗

标签:Java,队列,创建,任务,线程,new
From: https://www.cnblogs.com/aoCat/p/17973401

相关文章

  • 初识Java
    初识Java一场旷日持久的战争故事:Java帝国的诞生C&C++1972年C诞生贴近硬件,运行极快,效率极高。操作系统,编译器,数据库,网络系统等指针和内存管理1982年C++诞生面向对象兼容C图形领域、游戏等反抗我们要建立一个新的语言:语法有点像C没有指......
  • Js(Javascript)的apply call 和bind区别
    ​ apply、call和bind是用于调用函数的三种不同方式,它们的主要区别在于函数调用时的上下文(this关键字)以及参数传递的方式。call和apply是用于立即调用函数并设置this上下文的方法,它们的主要区别在于参数传递的方式。bind不会立即执行函数,而是创建一个新的函数,将this......
  • 线程同步之互斥锁
    目录如何使用Mutex中的lock与unlocktry_lock、try_lock_for和try_lock_untiltry_locktry_lock_fortry_lock_until如何使用Mutex中的lock与unlock在C++11中,您可以使用std::mutex中的lock和unlock函数来实现线程同步。lock函数用于锁定互斥量,而unlock函数用于解锁互斥量。下面是......
  • 《Java解惑》PDF
    本书特写了95个有关Java或其类库的陷阱和缺陷的谜题,其中大多数谜题都采用了短程序的方式,这些程序的行为与其看似的大相径庭。在每个谜题之后都给出了详细的解惑方案,这些解惑方案超越了对程序行为的简单解释,向读者展示了如何一劳永逸地避免底层的陷阱与缺陷。本书趣味十足、寓教于......
  • JAVA碎知识-2
    1、Set在Java中,Set 是一种集合接口(Interface),它继承自 Collection 接口,并且是一个无序、不重复的集合。不重复性:Set 中的元素是唯一的,不允许重复元素。Java中常用的 Set 实现类有以下几种:HashSet:基于哈希表实现,具有较快的插入、删除和查找操作。不保证元素的顺序。Tre......
  • Java学习日记 Day3 最难绷的一集
    JavaSE①LinkedList和ArrayList的区别:简单来说后者底层实现是数组,而前者是双向链表。②LinkedList的底层实现:对于集合的添加操作就是链表的操作原理,如果是空的添加,那么首尾指针都是当前节点,如果不是空,那就是当前的Last指针指向待添加节点,然后使Last指针指向该节点。而get方法的......
  • day 02java入门之Hello.java
    java命令行执行(注意代码编写用GBK,命令行窗口用GBK进行解析)注意public类名要和文件名一致,一个.java文件中最多只有一个public类java注意事项一个.java文件中若含有多个类时,编译完成后会生成相应个数的.class文件......
  • 线程的创建
    【一】threading模块介绍多线程创建和多进程创建很像我的理解是threading模块的作者遵循了鸭子类型所以和multiprocessing模块的使用方法那么像【二】开启线程的两种方式方式一直接调用Thread方法fromthreadingimportThreadimporttimedeftask(name):pr......
  • 对线程的理解
    【一】什么是线程线程可以被看作是在程序内部的一个独立的任务流,它是操作系统能够进行运算调度的最小单位。线程存在于进程之中,可以把进程想象成一个工厂,而线程就像是工厂里的工人。想象你有一个工厂(这个工厂就像一个进程),在这个工厂里有很多工人(这些工人就是线程)。这些工人......
  • 17_Java基础-文档注释+javadoc
    JavaDocjavados命令是用来生成自己API文档的参数信息:@author作者名@version版本号@since指明需要最早使用的jdk版本(开发这个程序所用的版本)@param参数名@return返回值情况@throws异常抛出情况Javadoc【java文件】通过命令行javadoc+参数生成java文件......