乐趣区

关于java:为什么要用线程池

线程池是一种治理和复用线程资源的机制,它由一个线程池管理器和一组工作线程组成。线程池管理器负责创立和销毁线程池,以及治理线程池中的工作线程。工作线程则负责执行具体的工作。

线程池的次要作用是治理和复用线程资源,防止了线程的频繁创立和销毁所带来的开销。
线程池蕴含两个重要的组成部分:

  1. 线程池大小:指线程池中所能包容的最大线程数。线程池大小个别依据零碎的负载状况和硬件资源来设置。
  2. 工作队列:用于寄存期待执行的工作。当线程池中的工作线程曾经全副被占用时,新的工作将被退出到工作队列中期待执行。

线程池创立形式

Java 中线程池的创立形式次要有以下几种:

  1. 应用 ThreadPoolExecutor 类手动创立:通过 ThreadPoolExecutor 类的构造函数自定义线程池的参数,包含外围线程数、最大线程数、线程存活工夫、工作队列等。
  2. 应用 Executors 类提供的工厂办法创立:通过 Executors 类提供的一些动态工厂办法创立线程池,例如 newFixedThreadPool、newSingleThreadExecutor、newCachedThreadPool 等。
  3. 应用 Spring 框架提供的 ThreadPoolTaskExecutor 类:在 Spring 框架中能够通过 ThreadPoolTaskExecutor 类来创立线程池。

不同的创立形式实用于不同的场景,通常能够依据理论状况抉择适合的形式创立线程池。手动创立 ThreadPoolExecutor 类能够灵便地配置线程池参数,但须要对线程池的各项参数有肯定的理解;应用 Executors 工厂办法能够疾速创立线程池,但可能无奈满足特定的需要,且容易呈现内存溢出的状况;而 Spring 框架提供的 ThreadPoolTaskExecutor 类则只能在 Spring 框架中应用。

线程池应用

ThreadPoolExecutor 应用

ThreadPoolExecutor 线程的创立与应用示例如下:

import java.util.concurrent.*;

public class ThreadPoolDemo {public static void main(String[] args) {
        // 创立线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2, // 外围线程数
            5, // 最大线程数
            60, // 线程池中线程的闲暇工夫(单位为秒)TimeUnit.SECONDS, // 工夫单位
            new ArrayBlockingQueue<>(10) // 工作队列
        );
        // 提交工作
        for (int i = 1; i <= 20; i++) {
            final int taskId = i;
            executor.submit(() -> {System.out.println("执行工作:" + taskId + ",线程名称:" + Thread.currentThread().getName());
                try {Thread.sleep(1000); // 模仿工作执行工夫
                } catch (InterruptedException e) {e.printStackTrace();
                }
            });
        }
        // 敞开线程池
        executor.shutdown();}
}

在下面的示例中,咱们通过 ThreadPoolExecutor 类手动创立了一个线程池,设置了线程池的外围线程数为 2,最大线程数为 5,线程闲暇工夫为 60 秒,工作队列为长度为 10 的 ArrayBlockingQueue。而后咱们通过 submit() 办法向线程池中提交了 20 个工作,每个工作会在执行时输入本人的编号和执行线程的名称,而后睡眠 1 秒钟模仿工作执行工夫。最初,咱们调用 shutdown() 办法敞开线程池。

Executors 应用

import java.util.concurrent.*;

public class ExecutorsDemo {public static void main(String[] args) {
        // 创立线程池
        ExecutorService executor = Executors.newFixedThreadPool(5);
        // 提交工作
        for (int i = 1; i <= 10; i++) {
            final int taskId = i;
            executor.submit(() -> {System.out.println("执行工作:" + taskId + ",线程名称:" + Thread.currentThread().getName());
                try {Thread.sleep(1000); // 模仿工作执行工夫
                } catch (InterruptedException e) {e.printStackTrace();
                }
            });
        }
        // 敞开线程池
        executor.shutdown();}
}

在下面的示例中,咱们应用了 Executors 工厂类中的 newFixedThreadPool() 办法创立了一个线程池,该线程池有 5 个固定线程。而后咱们通过 submit() 办法向线程池中提交了 10 个工作,每个工作会在执行时输入本人的编号和执行线程的名称,而后睡眠 1 秒钟模仿工作执行工夫。最初,咱们调用 shutdown() 办法敞开线程池。值得注意的是,应用 Executors 工厂类创立线程池尽管非常简单,然而在理论生产环境中并不举荐,因为这种形式很容易导致线程资源被耗尽,从而影响零碎的性能和稳定性。

ThreadPoolTaskExecutor 应用

Spring 中 ThreadPoolTaskExecutor 的应用示例如下:

import java.util.concurrent.*;

import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

public class ThreadPoolTaskExecutorDemo {public static void main(String[] args) {
        // 创立线程池
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2); // 外围线程数
        executor.setMaxPoolSize(5); // 最大线程数
        executor.setQueueCapacity(10); // 工作队列长度
        executor.initialize();
        // 提交工作
        for (int i = 1; i <= 20; i++) {
            final int taskId = i;
            executor.submit(() -> {System.out.println("执行工作:" + taskId + ",线程名称:" + Thread.currentThread().getName());
                try {Thread.sleep(1000); // 模仿工作执行工夫
                } catch (InterruptedException e) {e.printStackTrace();
                }
            });
        }
        // 敞开线程池
        executor.shutdown();}
}

在下面的示例中,咱们应用了 Spring 框架中的 ThreadPoolTaskExecutor 类创立了一个线程池,设置了线程池的外围线程数为 2,最大线程数为 5,工作队列长度为 10。而后咱们通过 submit() 办法向线程池中提交了 20 个工作,每个工作会在执行时输入本人的编号和执行线程的名称,而后睡眠 1 秒钟模仿工作执行工夫。最初,咱们调用 shutdown() 办法敞开线程池。留神,在应用 Spring 框架中的 ThreadPoolTaskExecutor 类创立线程池时,咱们须要先调用 initialize() 办法进行初始化。

ThreadPoolTaskExecutor 底层还是通过 JDK 中提供的 ThreadPoolExecutor 类实现的。

线程池长处详解

线程池相比于线程来说,它不须要频繁的创立和销毁线程,线程一旦创立之后,默认状况下就会始终放弃在线程池中,等到有工作来了,再用这些已有的线程来执行工作,如下图所示:

长处 1:复用线程,升高资源耗费

线程在创立时要开拓虚拟机栈、本地办法栈、程序计数器等公有线程的内存空间,而销毁时又要回收这些公有空间资源,如下图所示:

而线程池创立了线程之后就会放在线程池中,因而线程池相比于线程来说,第一个长处就是 能够复用线程、减低系统资源的耗费

长处 2:进步响应速度

线程池是复用已有线程来执行工作的,而线程是在有工作时才新建的,所以相比于线程来说,线程池可能更快的响应工作和执行工作。

长处 3:管控线程数和工作数

线程池提供了更多的治理性能,这里治理性能次要体现在以下两个方面:

  1. 管制最大并发数:线程池能够创立固定的线程数,从而防止了有限创立线程的问题。当线程创立过多时,会导致系统执行变慢,因为 CPU 核数是肯定的、能同时解决的工作数也是肯定的,而线程过多时就会造成线程歹意争抢和线程频繁切换的问题,从而导致程序执行变慢,所以适合的线程数才是高性能运行的要害。
  2. 管制工作最大数:如果工作有限多,而内存又有余的状况下,就会导致程序执行报错,而线程池能够管制最大工作数,当工作超过肯定数量之后,就会采纳回绝策略来解决多出的工作,从而保障了零碎能够衰弱的运行。

长处 4:更多加强性能

线程池相比于线程来说提供了更多的性能,比方定时执行和周期执行等性能。

小结

线程池是一种治理和复用线程资源的机制。相比于线程,它具备四个次要劣势:1. 复用线程,升高了资源耗费;2. 进步响应速度;3. 提供了治理线程数和工作数的能力;4. 更多加强性能。

本文已收录至《Java 面试突击》,专一 Java 面试 100 年,查看更多:www.javacn.site

退出移动版