【深入理解Java线程池】

深入理解Java线程池

Java线程池是Java并发编程中的一个重要概念,它提供了一种管理和复用线程的机制,可以显著减少创建和销毁线程的开销,提高系统的响应速度和吞吐量。以下是对Java线程池的详细解析:

一、线程池的基本概念

线程池是一个由多个线程组成的集合,这些线程可以被动态地分配给不同的任务。线程池中的线程在执行完一个任务后,并不会被销毁,而是会回到线程池中等待执行下一个任务。这样可以避免频繁地创建和销毁线程,提高系统的性能。

二、线程池的核心参数

  1. corePoolSize(核心线程数):线程池中始终保持的线程数量,即使这些线程处于空闲状态。
  2. maximumPoolSize(最大线程数) :线程池能够容纳的最大线程数。当工作队列满时,线程池会尝试创建新的线程,直到线程数量达到maximumPoolSize
  3. keepAliveTime(线程空闲时间) :当线程数量超过corePoolSize时,这是多余空闲线程在终止前等待新任务的最长时间。
  4. unit(时间单位) :用于指定keepAliveTime的时间单位。
  5. workQueue(任务队列):用于保存等待执行的任务的阻塞队列。
  6. threadFactory(线程工厂):用于创建新线程的工厂。通过线程工厂可以给每个创建出来的线程设置更有意义的名字。
  7. handler(拒绝策略) :当队列和线程池都满了,说明线程池处于饱和状态,必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。还可以选择CallerRunsPolicy(用调用者所在线程来运行任务)、DiscardPolicy(丢弃队列里最近的一个任务,并执行当前任务)、DiscardOldestPolicy(丢弃最老的任务,尝试重新提交新的任务)等策略,或者根据应用场景实现RejectedExecutionHandler接口自定义策略。

三、线程池的工作流程

  1. 提交任务 :任务通过execute(Runnable command)submit(Callable<T> task)方法提交到线程池。
  2. 判断核心线程是否空闲 :如果线程池中的核心线程数量少于corePoolSize,则创建新的核心线程来执行任务。
  3. 放入任务队列 :如果核心线程都在忙,则任务会被放入workQueue中等待执行。
  4. 创建非核心线程 :如果workQueue已满,且当前线程数量少于maximumPoolSize,则线程池会尝试创建新的非核心线程来执行任务。
  5. 拒绝任务 :如果workQueue已满,且线程数量已达到maximumPoolSize,则线程池会根据拒绝策略拒绝新任务。
  6. 线程回收 :当线程数量超过corePoolSize,且空闲线程的空闲时间超过keepAliveTime,则这些线程会被回收。

四、常见的线程池类型

  1. FixedThreadPool(固定大小线程池):包含固定数量的线程,适用于需要限制并发线程数量的场景。
  2. CachedThreadPool(缓存线程池):不固定线程数量,可以根据需要自动创建新线程,适用于执行大量短期异步任务。
  3. SingleThreadPool(单线程池):只包含一个工作线程,保证所有任务按顺序执行,适用于需要保持任务顺序执行的场景。
  4. ScheduledThreadPool(定时线程池):可以执行定时任务和周期性任务。
  5. WorkStealingPool(工作窃取线程池):Java 8中引入的一种新类型的线程池,主要用于处理耗时任务,适用于需要大量并行任务、任务之间没有依赖关系的情况。

五、线程池的使用方法和注意事项

  1. 创建线程池 :可以使用ThreadPoolExecutor类来创建线程池,并设置相应的参数。也可以使用Executors工厂类来方便地创建常见的线程池类型。
  2. 提交任务 :通过execute()submit()方法将任务提交到线程池。
  3. 关闭线程池 :线程池应该通过shutdown()shutdownNow()方法显式关闭。shutdown()方法会平滑地关闭线程池,不再接受新任务,但会继续执行队列中的任务。shutdownNow()方法会尝试立即关闭线程池,并尝试停止正在执行的任务。
  4. 注意事项
    • 避免创建过多的线程,合理配置线程池的大小。
    • 任务执行时间不宜过长,以免影响线程池的响应速度。
    • 确保提交给线程池的任务是线程安全的。
    • 根据应用需求合理设置线程池的参数。

六、示例代码

使用ThreadPoolExecutor创建线程池并提交任务的示例代码:

java 复制代码
import java.util.concurrent.*;

public class ThreadPoolExample {
    public static void main(String[] args) {
        // 创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2,  // 核心线程数
            4,  // 最大线程数
            60L, TimeUnit.SECONDS,  // 线程空闲时间和时间单位
            new LinkedBlockingQueue<Runnable>()
        );

        // 向线程池提交任务
        for (int i = 0; i < 10; i++) {
            final int task = i;
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + " 正在执行第 " + (task + 1) + " 个任务");
                }
            });
        }

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

使用Executors工厂类创建不同类型线程池

java 复制代码
import java.util.concurrent.*;

public class ExecutorsExample {
    public static void main(String[] args) throws InterruptedException {
        // 创建一个固定大小的线程池
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
        // 创建一个缓存线程池
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        // 创建一个单线程线程池
        ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
        // 创建一个定时线程池
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(2);
        // 创建一个工作窃取线程池
        ExecutorService workStealingPool = Executors.newWorkStealingPool();

        // 提交任务给不同类型的线程池
        for (int i = 0; i < 5; i++) {
            final int taskIndex = i;
            fixedThreadPool.submit(() -> printTaskInfo("Fixed", taskIndex));
            cachedThreadPool.submit(() -> printTaskInfo("Cached", taskIndex));
            singleThreadPool.submit(() -> printTaskInfo("Single", taskIndex));
            scheduledThreadPool.schedule(() -> printTaskInfo("Scheduled", taskIndex), 2, TimeUnit.SECONDS); // 延迟2秒执行
        }

        // 关闭所有线程池
        fixedThreadPool.shutdown();
        cachedThreadPool.shutdown();
        singleThreadPool.shutdown();
        scheduledThreadPool.shutdown();
        workStealingPool.shutdown();
    }

    private static void printTaskInfo(String poolType, int taskIndex) {
        System.out.println(poolType + " Thread Pool: " + Thread.currentThread().getName() + " is executing Task-" + taskIndex);
    }
}

使用自定义线程工厂和拒绝策略

java 复制代码
import java.util.concurrent.*;

public class CustomThreadPoolExample {
    public static void main(String[] args) {
        // 定义自定义线程工厂
        ThreadFactory namedThreadFactory = new ThreadFactory() {
            private int count = 0;

            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "MyCustomThread-" + count++);
            }
        };

        // 定义自定义拒绝策略
        RejectedExecutionHandler customRejectedHandler = new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                System.err.println("Task " + r.toString() + " rejected from " + executor.toString());
            }
        };

        // 创建带有自定义线程工厂和拒绝策略的线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2, 4, 60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(10),
            namedThreadFactory,
            customRejectedHandler
        );

        // 提交大量任务测试自定义拒绝策略
        for (int i = 0; i < 20; i++) {
            final int taskNumber = i;
            executor.execute(() -> printTaskInfo("Custom", taskNumber));
        }

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

    private static void printTaskInfo(String poolType, int taskIndex) {
        System.out.println(poolType + " Thread Pool: " + Thread.currentThread().getName() + " is executing Task-" + taskIndex);
    }
}

使用submit()方法提交可返回结果的任务

java 复制代码
import java.util.concurrent.*;

public class CallableExample {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        // 创建线程池
        ExecutorService executor = Executors.newSingleThreadExecutor();

        // 提交Callable任务,该任务可以返回结果
        Future<Integer> future = executor.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                // 模拟耗时操作
                TimeUnit.SECONDS.sleep(2);
                return 123;
            }
        });

        // 获取任务的结果
        try {
            Integer result = future.get(); // 阻塞直到任务完成
            System.out.println("Task returned: " + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } finally {
            // 关闭线程池
            executor.shutdown();
        }
    }
}

使用invokeAll()方法提交多个任务并获取结果

java 复制代码
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public class InvokeAllExample {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        // 创建线程池
        ExecutorService executor = Executors.newFixedThreadPool(3);

        // 创建多个Callable任务
        List<Callable<String>> tasks = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            final int taskId = i;
            tasks.add(() -> {
                // 模拟耗时操作
                TimeUnit.SECONDS.sleep(1);
                return "Result of Task-" + taskId;
            });
        }

        // 提交所有任务并等待所有任务完成
        List<Future<String>> futures = executor.invokeAll(tasks);

        // 获取所有任务的结果
        for (Future<String> future : futures) {
            System.out.println(future.get()); // 阻塞直到每个任务完成
        }

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

以上就是对Java线程池的详细解析,包括线程池的基本概念、核心参数、工作流程、常见类型、使用方法和注意事项等内容。通过深入理解线程池,我们可以更好地利用线程池来提高并发应用的性能和可靠性。

相关推荐
吴冰_hogan9 分钟前
MySQL事务隔离
数据库·mysql
练小杰13 分钟前
我在广州学 Mysql 系列之 数据“表”的基本操作
android·数据库·学习·mysql·adb
呆呆在发呆.15 分钟前
计组实验-Bomb Lab -2
java·服务器·前端
gsforget32116 分钟前
ORACLE RAC ADG备库报错ORA-04021: timeout occurred while waiting to lock object
数据库·oracle·oracle adg
Yvemil721 分钟前
数据库镜像(Database Mirroring):高可用性与灾难恢复技术
数据库·oracle
老马啸西风21 分钟前
开源分布式系统追踪-01-Zipkin-01-入门介绍
java
工一木子27 分钟前
【Leecode】Leecode刷题之路第82天之删除排序链表中的重复元素II
java·数据结构·算法·leetcode·链表
一只小灿灿31 分钟前
计算机视觉中的图像滤波与增强算法
人工智能·算法·计算机视觉
Illusionna.36 分钟前
Word2Vec 模型 PyTorch 实现并复现论文中的数据集
人工智能·pytorch·算法·自然语言处理·nlp·matplotlib·word2vec
平凡灵感码头41 分钟前
机器学习算法概览
人工智能·算法·机器学习