如何设置Java线程池大小?

简介: 线程是调度 CPU 资源的最小单位, 线程模型粉丝 KLT 模型与 ULT 模型,JVM 使用的是 KLT 模型, Java线程与 OS线程保持 1:1 的映射关系,也就是说有一个 java 线程也会在操作系统中有一个对应的线程, Java 线程有多种生命状态

线程的概念


线程


线程是调度 CPU 资源的最小单位, 线程模型粉丝 KLT 模型与 ULT 模型,JVM 使用的是 KLT 模型, Java线程与 OS线程保持 1:1 的映射关系,也就是说有一个 java 线程也会在操作系统中有一个对应的线程, Java 线程有多种生命状态


// 线程状态枚举
public enum State {
  NEW, // 创建,调用 start()
  RUNNABLE, // 就绪
  BLOCKED, // 阻塞, 竞争没有拿到锁
  WAITING, // 等待
  TIMED_WAITING, // 超时等待
  TERMINATED; // 终止
}


协程


协程,Java的设计者们也没能为开发人员在API层面提供对协程的支持。因此,如果我们想在程序中使用协程来提升特定场景下应用程序的执行性能则只能依赖于第三方开源构件(比如:quasarcoroutineskilim等),或是采用混合编程(比如:Kotlin)的方式进行。


Java 线程池


线程池(Thread Pool)是一种基于池化思想管理线程的工具,经常出现在多线程服务器中,如MySQL。


线程过多会带来额外的开销,其中包括创建销毁线程的开销、调度线程的开销等等,同时也降低了计算机的整体性能。线程池维护多个线程,等待监督管理者分配可并发执行的任务。这种做法,一方面避免了处理任务时创建销毁线程开销的代价,另一方面避免了线程数量膨胀导致的过分调度问题,保证了对内核的充分利用。


线程池优点


合理地使用线程池能够带来3个好处:


  1. 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。


  1. 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。


  1. 提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源, 还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。但是,要做到合理利用 线程池,必须对其实现原理了如指掌。


创建参数


我们可以通过ThreadPoolExecutor来创建一个线程池。它的构造方法如下:


new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime,
   milliseconds,runnableTaskQueue, handler);


创建一个线程池时需要输入如下几个参数:


corePoolSize(线程池的基本大小) : 当提交一个任务到线程池时,线程池会创建一个线 程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任 务数大于线程池基本大小时就不再创建。如果调用了线程池的prestartAllCoreThreads()方法, 线程池会提前创建并启动所有基本线程。


runnableTaskQueue(任务队列) :用于保存等待执行的任务的阻塞队列。可以选择以下几个阻塞队列。


  1. ArrayBlockingQueue: 是一个基于数组结构的有界阻塞队列,此队列按FIFO(先进先出)原 则对元素进行排序。


  1. LinkedBlockingQueue: 一个基于链表结构的阻塞队列,此队列按FIFO排序元素,吞吐量通 常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列


  1. SynchronousQueue: 一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用 移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于Linked-BlockingQueue,静态工 厂方法Executors.newCachedThreadPool使用了这个队列。


  1. PriorityBlockingQueue: 一个具有优先级的无限阻塞队列。


maximumPoolSize(线程池最大数量): 线程池允许创建的最大线程数。如果队列满了,并 且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是,如 果使用了无界的任务队列这个参数就没什么效果。


ThreadFactory: 用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设 置更有意义的名字。使用开源框架guava提供的ThreadFactoryBuilder可以快速给线程池里的线 程设置有意义的名字,代码如下。


new ThreadFactoryBuilder().setNameFormat("XX-task-%d").build();


RejectedExecutionHandler(饱和策略): 当队列和线程池都满了,说明线程池处于饱和状 态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法 处理新任务时抛出异常。在JDK 1.5中Java线程池框架提供了以下4种策略。


  1. AbortPolicy: 直接抛出异常。


  1. CallerRunsPolicy: 只用调用者所在线程来运行任务。


  1. DiscardOldestPolicy: 丢弃队列里最近的一个任务,并执行当前任务。


  1. DiscardPolicy: 不处理,丢弃掉。


当然,也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略。如记录 日志或持久化存储不能处理的任务。


keepAliveTime(线程活动保持时间): 线程池的工作线程空闲后,保持存活的时间。所以, 如果任务很多,并且每个任务执行的时间比较短,可以调大时间,提高线程的利用率。


TimeUnit(线程活动保持时间的单位): 可选的单位有天(DAYS)、小时(HOURS)、分钟 (MINUTES)、毫秒(MILLISECONDS)、微秒(MICROSECONDS,千分之一毫秒)和纳秒(NANOSECONDS,千分之一微秒)。


工作原理


通过上面几个参数的描述梳理,让我们大致了解了如何初始化一个线程池,对于线程池任务的提交和执行如下图所示:


image.png


代码示例


下面是一个创建线程池和调用的的简单例子,供大家参考:


// 创建线程池
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolTest {
    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                2, 4, 0, TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(10),
                new ThreadFactory() {
                    int i = 0;
                    @Override
                    public Thread newThread(Runnable r) {
                        Thread thread = new Thread(r);
                        thread.setName("pool-test-" + i++);
                        return thread;
                    }
                },
                // CallerRunsPolicy 如果当前线程池没有关闭会调用当前提交任务的线程参与执行
                new ThreadPoolExecutor.CallerRunsPolicy()
        );
        for (int i = 0; i < 100; i++) {
            threadPool.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    String name = Thread.currentThread().getName();
                    if ("main".equals(name)) {
                        // CallerRunsPolicy 策略 提交任务线程参与执行
                        System.out.println("this is a idea debug line");
                    }
                    System.out.println(name + ": " + this.toString());
                }
            });
        }
        threadPool.shutdown();
        while (true) {
            if (threadPool.isTerminated()) {
                System.out.println("所有的子线程都结束了!");
                break;
            }
            Thread.sleep(100);
        }
    }
}


线程数设置参考


  1. CPU 密集型: CPU 核数  + 1


  1. IO 密集型: 2 倍 CPU 核数 + 1


参考资料




相关文章
|
2月前
|
Java 调度 数据库
Java并发编程:深入理解线程池
在Java并发编程的海洋中,线程池是一艘强大的船,它不仅提高了性能,还简化了代码结构。本文将带你潜入线程池的深海,探索其核心组件、工作原理及如何高效利用线程池来优化你的并发应用。
|
2月前
|
存储 监控 Java
Java多线程优化:提高线程池性能的技巧与实践
Java多线程优化:提高线程池性能的技巧与实践
70 1
|
19天前
|
Java Spring
spring多线程实现+合理设置最大线程数和核心线程数
本文介绍了手动设置线程池时的最大线程数和核心线程数配置方法,建议根据CPU核数及程序类型(CPU密集型或IO密集型)来合理设定。对于IO密集型,核心线程数设为CPU核数的两倍;CPU密集型则设为CPU核数加一。此外,还讨论了`maxPoolSize`、`keepAliveTime`、`allowCoreThreadTimeout`和`queueCapacity`等参数的设置策略,以确保线程池高效稳定运行。
87 10
spring多线程实现+合理设置最大线程数和核心线程数
|
1月前
|
Java 调度 开发者
Java并发编程:深入理解线程池
在Java的世界中,线程池是提升应用性能、实现高效并发处理的关键工具。本文将深入浅出地介绍线程池的核心概念、工作原理以及如何在实际应用中有效利用线程池来优化资源管理和任务调度。通过本文的学习,读者能够掌握线程池的基本使用技巧,并理解其背后的设计哲学。
|
1月前
|
缓存 监控 Java
Java中的并发编程:理解并应用线程池
在Java的并发编程中,线程池是提高应用程序性能的关键工具。本文将深入探讨如何有效利用线程池来管理资源、提升效率和简化代码结构。我们将从基础概念出发,逐步介绍线程池的配置、使用场景以及最佳实践,帮助开发者更好地掌握并发编程的核心技巧。
|
1月前
|
缓存 监控 Java
java中线程池的使用
java中线程池的使用
|
11天前
|
Java 数据中心 微服务
Java高级知识:线程池隔离与信号量隔离的实战应用
在Java并发编程中,线程池隔离与信号量隔离是两种常用的资源隔离技术,它们在提高系统稳定性、防止系统过载方面发挥着重要作用。
13 0
|
16天前
|
存储 缓存 Java
JAVA并发编程系列(11)线程池底层原理架构剖析
本文详细解析了Java线程池的核心参数及其意义,包括核心线程数量(corePoolSize)、最大线程数量(maximumPoolSize)、线程空闲时间(keepAliveTime)、任务存储队列(workQueue)、线程工厂(threadFactory)及拒绝策略(handler)。此外,还介绍了四种常见的线程池:可缓存线程池(newCachedThreadPool)、定时调度线程池(newScheduledThreadPool)、单线程池(newSingleThreadExecutor)及固定长度线程池(newFixedThreadPool)。
|
2月前
|
JSON Java API
【Azure API 管理】通过Java APIM SDK创建一个新的API,如何为Reqeust的Representation设置一个内容示例(Sample)?
【Azure API 管理】通过Java APIM SDK创建一个新的API,如何为Reqeust的Representation设置一个内容示例(Sample)?
|
2月前
|
缓存 前端开发 Java
【Azure 应用服务】App Service 使用Tomcat运行Java应用,如何设置前端网页缓存的相应参数呢(-Xms512m -Xmx1204m)?
【Azure 应用服务】App Service 使用Tomcat运行Java应用,如何设置前端网页缓存的相应参数呢(-Xms512m -Xmx1204m)?