Java线程池

简介: Java线程池

概念

线程池(Thread Pool)是Java中用于管理线程的一种技术,它可以有效地提高程序的并发性能和资源利用率。

它是一种多线程处理形式,线程池预先创建并维护一组线程,用于处理任务队列中的任务。线程池中的线程可以被重复利用,当有新任务到达时,线程池会分配一个线程来执行任务,而不是每次都创建新的线程。线程池中的线程数量通常是有限的,这可以避免因为创建过多线程而导致系统资源耗尽。

线程池可以看作是一个管理线程的“总调度官”,它负责线程的存储、调度和执行。线程池内部通常包含核心线程和非核心线程,核心线程是长期存在的,而非核心线程则根据任务的数量动态创建和销毁。

线程池的主要优点

降低资源消耗

线程的创建和销毁需要消耗大量的系统资源。通过线程池,我们可以复用已经创建的线程,从而避免频繁地创建和销毁线程。

提高响应速度

当任务到达时,线程池可以立即分配一个线程来处理任务,从而提高了系统的响应速度。

提高系统吞吐量

通过合理地配置线程池的大小,我们可以充分利用系统资源,从而提高系统的吞吐量。

Java中线程池的实现

FixedThreadPool

特点

创建一个固定大小的线程池,当有新任务提交时,如果线程池中有空闲线程,则立即执行;如果线程池已满,则任务会在队列中等待,直到有空闲线程可用。
线程池的大小在创建时指定,之后不能更改。
工作队列是一个无界队列,可以容纳任意数量的待执行任务。
这是很常用的一个线程池,因为它的容量由程序员决定,所以管理起来也比价容易。

适用场景

适用于已知并发任务数量,且任务数量不会变化或变化不大的场景。
当需要减少在创建和销毁线程上花费的时间以及系统开销时,可以考虑使用FixedThreadPool。

注意事项

由于工作队列是无界的,如果任务提交速度持续大于处理速度,可能会导致内存耗尽。

使用

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

public class FixedThreadPoolExample {
   
   
    public static void main(String[] args) {
   
   
        int threadSize = 10000; // 线程数量大小
        long during = 0; // 耗费时间

        long start = System.currentTimeMillis();

        // 创建一个固定大小为3的线程池
        ExecutorService executor = Executors.newFixedThreadPool(3);

        // 提交5个任务到线程池
        for (int i = 0; i < threadSize; i++) {
   
   
            int taskId = i;
            executor.submit(() -> {
   
   
                try {
   
   
                    Thread.sleep(1);
                } catch (InterruptedException e) {
   
   
                    throw new RuntimeException(e);
                }
                System.out.println("执行任务 " + taskId + ",由线程 " + Thread.currentThread().getName() + " 执行");
            });
        }

        // 关闭线程池(这会等待已提交的任务执行完毕)
        executor.shutdown();
        while (!executor.isTerminated()) {
   
   
            // 等待所有任务执行完毕
        }

        System.out.println("所有任务执行完毕");
        long end = System.currentTimeMillis();
        during = end - start; // 记录线程池的时间


        start = System.currentTimeMillis();
        for (int i = 0; i < threadSize; i++) {
   
   
            int taskId = i;
            Thread thread = new Thread(() -> {
   
   
                try {
   
   
                    Thread.sleep(1);
                } catch (InterruptedException e) {
   
   
                    throw new RuntimeException(e);
                }
                System.out.println("执行任务 " + taskId + ",由线程 " + Thread.currentThread().getName() + " 执行");
            });
            thread.start();
            try {
   
   
                thread.join();
            } catch (InterruptedException e) {
   
   
                throw new RuntimeException(e);
            }
        }

        System.out.println("所有任务执行完毕");
        end = System.currentTimeMillis();

        System.out.println("线程池耗时" + (during) + "ms");
        System.out.println("线程耗时" + (end - start) + "ms");

        System.out.println("线程池相比线程更" + ((end - start) > during ? "快" : "慢"));
    }
}

最后的结果输出如下

所有任务执行完毕
线程池耗时6172ms
线程耗时21764ms
线程池相比线程更快

如果你把线程池的线程数量调成9,则结果如下

所有任务执行完毕
线程池耗时2002ms
线程耗时21542ms
线程池相比线程更快

CachedThreadPool

特点

创建一个可缓存的线程池,如果线程池中有空闲线程,则立即执行;如果没有空闲线程,则创建一个新线程来执行任务。
线程池的大小会根据需要动态调整
工作队列是一个SynchronousQueue,它实际上不存储元素,每个插入操作必须等待一个相应的删除操作。

适用场景

适用于执行大量短时间异步任务的程序。
当任务数量不确定,且任务执行时间较短时,可以考虑使用CachedThreadPool。

注意事项

由于线程池的大小可以无限制增长,如果任务执行时间很长,或者任务数量持续很大,可能会导致系统资源耗尽。

使用

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

public class CachedThreadPoolExample {
   
   
    public static void main(String[] args) throws InterruptedException {
   
   

        Thread.sleep(20000); // 方便jconsole查看此进程

        int threadSize = 10000; // 线程数量大小
        long during = 0; // 耗费时间

        long start = System.currentTimeMillis();

        // 创建一个固定大小为3的线程池
        ExecutorService executor = Executors.newCachedThreadPool();

        // 提交5个任务到线程池
        for (int i = 0; i < threadSize; i++) {
   
   
            int taskId = i;
            executor.submit(() -> {
   
   
                try {
   
   
                    Thread.sleep(5);
                } catch (InterruptedException e) {
   
   
                    throw new RuntimeException(e);
                }

                System.out.println("执行任务 " + taskId + ",由线程 " + Thread.currentThread().getName() + " 执行");
            });
        }

        // 关闭线程池(这会等待已提交的任务执行完毕)
        executor.shutdown();
        while (!executor.isTerminated()) {
   
   
            // 等待所有任务执行完毕
        }

        System.out.println("所有任务执行完毕");
        long end = System.currentTimeMillis();
        during = end - start; // 记录线程池的时间

        Thread.sleep(20000);

        start = System.currentTimeMillis();
        for (int i = 0; i < threadSize; i++) {
   
   
            int taskId = i;
            Thread thread = new Thread(() -> {
   
   
                try {
   
   
                    Thread.sleep(5);
                } catch (InterruptedException e) {
   
   
                    throw new RuntimeException(e);
                }
                System.out.println("执行任务 " + taskId + ",由线程 " + Thread.currentThread().getName() + " 执行");
            });
            thread.start();
            thread.join();
        }

        System.out.println("所有任务执行完毕");
        end = System.currentTimeMillis();

        System.out.println("线程池耗时" + (during) + "ms");
        System.out.println("线程耗时" + (end - start) + "ms");

        System.out.println("线程池相比线程更" + ((end - start) > during ? "快" : "慢"));

    }
}

控制台末尾的输出如下

所有任务执行完毕
线程池耗时468ms
线程耗时59495ms
线程池相比线程更快

jconsole的性能监控如下
在这里插入图片描述

ScheduledThreadPool

特点

创建一个可以执行定时或周期性任务的线程池。
线程池的大小在创建时指定,之后不能更改。
工作队列是一个DelayedWorkQueue,它用于存放待执行的任务,并根据任务的延迟时间进行排序。

适用场景

适用于需要定时执行或周期性执行任务的场景。
当需要按照特定时间间隔执行任务,或者需要在特定时间执行任务时,可以考虑使用ScheduledThreadPool。

注意事项

由于线程池的大小是固定的,如果任务数量很大,可能会因为线程资源不足而导致任务延迟执行。

使用

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

public class ScheduledThreadPoolExample {
   
   
    public static void main(String[] args) {
   
   
        // 创建一个定时线程池
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);

        // 在延迟2秒后执行任务,并每隔1秒重复执行
        executor.scheduleAtFixedRate(() -> {
   
   
            System.out.println("执行定时任务,由线程 " + Thread.currentThread().getName() + " 执行");
        }, 2, 1, TimeUnit.SECONDS);

        // 让主线程等待足够长的时间,以便观察定时任务的执行
        try {
   
   
            Thread.sleep(10000);
        } catch (InterruptedException e) {
   
   
            e.printStackTrace();
        }

        // 关闭线程池
        executor.shutdown();
    }
}

console输出

执行定时任务,由线程 pool-1-thread-1 执行
执行定时任务,由线程 pool-1-thread-1 执行
执行定时任务,由线程 pool-1-thread-2 执行
执行定时任务,由线程 pool-1-thread-2 执行
执行定时任务,由线程 pool-1-thread-2 执行

SingleThreadExecutor

特点

创建一个只有一个线程的线程池,所有任务都会按照提交的顺序依次执行。
保证了任务的执行顺序,即任务的提交顺序和执行顺序是一致的。
工作队列是一个LinkedBlockingQueue,用于存放待执行的任务。

适用场景

适用于需要保证任务按照提交顺序执行的场景。
当任务之间没有依赖关系,但需要保证任务的执行顺序时,可以考虑使用SingleThreadExecutor。

注意事项

由于只有一个线程,如果任务执行时间很长,可能会导致后续任务等待时间过长。

使用

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

public class SingleThreadExecutorExample {
   
     
    public static void main(String[] args) {
   
     
        // 创建一个单线程的线程池  
        ExecutorService executor = Executors.newSingleThreadExecutor();  

        // 提交5个任务到线程池  
        for (int i = 0; i < 5; i++) {
   
     
            final int taskId = i;  
            executor.submit(() -> {
   
     
                System.out.println("执行任务 " + taskId + ",由线程 " + Thread.currentThread().getName() + " 执行");  
            });  
        }  

        // 关闭线程池  
        executor.shutdown();  
        while (!executor.isTerminated()) {
   
     
            // 等待所有任务执行完毕  
        }  
        System.out.println("所有任务执行完毕");  
    }  
}

console输出

执行任务 0,由线程 pool-1-thread-1 执行
执行任务 1,由线程 pool-1-thread-1 执行
执行任务 2,由线程 pool-1-thread-1 执行
执行任务 3,由线程 pool-1-thread-1 执行
执行任务 4,由线程 pool-1-thread-1 执行
所有任务执行完毕

语法总结

提交任务

Future<?> submit(Runnable task): 提交一个Runnable任务进行执行,并返回一个表示异步计算结果的Future对象。

Future submit(Callable task): 提交一个Callable任务进行执行,并返回一个表示异步计算结果的Future对象。Callable可以返回一个结果。
Future<?> submit(Runnable task, T result): 提交一个Runnable任务进行执行,并返回一个表示异步计算结果的Future对象,该对象在任务完成时返回给定的结果。

关闭线程池

void shutdown(): 启动一次顺序关闭,执行以前提交的任务,但不接受新任务。如果已经关闭,则调用没有其他效果。
List shutdownNow(): 试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。

检查线程池状态

boolean isShutdown(): 如果执行器已关闭,则返回true。
boolean isTerminated(): 如果执行器已关闭,并且所有任务都已完成,则返回true。
boolean awaitTermination(long timeout, TimeUnit unit): 如果在给定的超时时间内,执行器终止,则返回true。

import java.util.concurrent.*;

public class ThreadPoolExample {
   
   
    public static void main(String[] args) {
   
   
        // 创建一个固定大小的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(5);

        // 提交任务到线程池
        Future<String> future1 = executorService.submit(() -> {
   
   
            return "Task 1 result returned successfully";
        });

        Future<Integer> future2 = executorService.submit(() -> {
   
   
            // 模拟耗时任务
            try {
   
   
                Thread.sleep(1000);
            } catch (InterruptedException e) {
   
   
                e.printStackTrace();
            }
            return 42; // 返回计算结果
        });



        // 提交10个任务到线程池
        for (int i = 0; i < 10; i++) {
   
   
            final int taskId = i;
            executorService.submit(() -> {
   
   
                System.out.println("Task " + taskId + " is running by thread " + Thread.currentThread().getName());
                try {
   
   
                    // 模拟任务执行时间
                    Thread.sleep((long) (Math.random() * 1000));
                } catch (InterruptedException e) {
   
   
                    e.printStackTrace();
                }
            });
        }

        // 关闭线程池
        executorService.shutdown();
        System.out.println("线程池关闭----");

        // 等待任务完成并获取结果
        try {
   
   
            System.out.println("Task 1 result: future1 -> " + future1.get());
            System.out.println("Task 2 result: future2 -> " + future2.get());
        } catch (Exception e) {
   
   
            e.printStackTrace();
        }
        // 关闭线程池并开始等待其终止
        try {
   
   
            // 等待10秒,看线程池是否能在10秒内完成所有任务并终止
            if (executorService.awaitTermination(10, TimeUnit.SECONDS)) {
   
   
                System.out.println("All tasks have completed execution and the executor service has terminated.");
            } else {
   
   
                System.out.println("The executor service did not terminate within 10 seconds.");
            }
        } catch (InterruptedException e) {
   
   
            e.printStackTrace();
        }
    }
}
目录
相关文章
|
7天前
|
Java 数据库
【Java多线程】对线程池的理解并模拟实现线程池
【Java多线程】对线程池的理解并模拟实现线程池
20 1
|
7天前
|
Java
Java并发编程:深入理解线程池
【4月更文挑战第30天】本文将深入探讨Java并发编程中的一个重要主题——线程池。我们将从线程池的基本概念入手,了解其工作原理和优势,然后详细介绍如何使用Java的Executor框架创建和管理线程池。最后,我们将讨论一些高级主题,如自定义线程工厂和拒绝策略。通过本文的学习,你将能够更好地理解和使用Java的线程池,提高你的并发编程能力。
|
7天前
|
Java 程序员 数据库
Java线程池让使用线程变得更加高效
使用一个线程需要经过创建、运行、销毁三大步骤,如果业务系统每个线程都要经历这个过程,那会带来过多不必要的资源消耗。线程池就是为了解决这个问题而生,需要时就从池中拿取,使用完毕就放回去,池化思想通过复用对象大大提高了系统的性能。线程池、数据库连接池、对象池等都采用了池化技术,下面我们就来学习下线程池的核心知识、面试重点~
62 5
Java线程池让使用线程变得更加高效
|
1天前
|
Java 容器
Java并发编程:深入理解线程池
【5月更文挑战第21天】 在多核处理器的普及下,并发编程成为了提高程序性能的重要手段。Java提供了丰富的并发工具,其中线程池是管理线程资源、提高系统响应速度和吞吐量的关键技术。本文将深入探讨线程池的核心原理、关键参数及其调优策略,并通过实例展示如何高效地使用线程池以优化Java应用的性能。
|
1天前
|
监控 算法 Java
Java并发编程:深入理解线程池
【5月更文挑战第21天】 在现代软件开发中,尤其是Java应用中,并发编程是一个不可忽视的重要领域。合理利用多线程可以显著提高程序的性能和响应速度。本文将深入探讨Java中的线程池机制,包括其工作原理、优势以及如何正确使用线程池来优化应用程序性能。通过分析线程池的核心参数配置,我们将了解如何根据不同的应用场景调整线程池策略,以期达到最佳的并发处理效果。
|
1天前
|
Java 调度 开发者
Java并发编程:深入理解线程池
【5月更文挑战第21天】本文旨在通过深入探讨Java并发编程的核心组件——线程池,为开发者提供对线程池的全面理解。我们将从线程池的基本概念、优势入手,逐步深入到线程池的核心原理、常用配置参数,以及如何合理地使用线程池来提高系统性能和稳定性。文章将结合实际案例,帮助读者掌握线程池的使用技巧,以及在面对不同场景时如何进行调优。
|
4天前
|
Java
深入理解Java并发编程:线程池的应用与优化
【5月更文挑战第18天】本文将深入探讨Java并发编程中的重要概念——线程池。我们将了解线程池的基本概念,应用场景,以及如何优化线程池的性能。通过实例分析,我们将看到线程池如何提高系统性能,减少资源消耗,并提高系统的响应速度。
14 5
|
4天前
|
存储 Java
【Java】实现一个简单的线程池
,如果被消耗完了就说明在规定时间内获取不到任务,直接return结束线程。
11 0
|
7天前
|
Java 调度
Java一分钟之线程池:ExecutorService与Future
【5月更文挑战第12天】Java并发编程中,`ExecutorService`和`Future`是关键组件,简化多线程并提供异步执行能力。`ExecutorService`是线程池接口,用于提交任务到线程池,如`ThreadPoolExecutor`和`ScheduledThreadPoolExecutor`。通过`submit()`提交任务并返回`Future`对象,可检查任务状态、获取结果或取消任务。注意处理`ExecutionException`和避免无限等待。实战示例展示了如何异步执行任务并获取结果。理解这些概念对提升并发性能至关重要。
21 5
|
7天前
|
Java 调度
Java并发编程:深入理解线程池
【5月更文挑战第11天】本文将深入探讨Java中的线程池,包括其基本概念、工作原理以及如何使用。我们将通过实例来解释线程池的优点,如提高性能和资源利用率,以及如何避免常见的并发问题。我们还将讨论Java中线程池的实现,包括Executor框架和ThreadPoolExecutor类,并展示如何创建和管理线程池。最后,我们将讨论线程池的一些高级特性,如任务调度、线程优先级和异常处理。