首页 > 编程语言 >Java线程池与异常处理

Java线程池与异常处理

时间:2023-06-17 12:12:17浏览次数:46  
标签:Java 队列 任务 handler 线程 new 异常 ThreadPoolExecutor

线程池

线程池的创建代码

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler);

参数的含义

corePoolSize —— 保留在池中的线程数,即使它们处于空闲状态也不会销毁,除非设置了allowCoreThreadTimeOut
maximumPoolSize —— 池中允许的最大线程数
keepAliveTime —— 当线程数大于核心数时,多余的空闲线程在终止前等待新任务的最长时间。
unit —— keepAliveTime参数的时间单位
workQueue —— 在执行任务之前用于保存任务的队列。该队列将仅保存由execute方法提交的Runnable任务。
threadFactory —— 执行者创建新线程时使用的工厂
handler —— 由于达到线程边界和队列容量而阻塞执行时使用的处理程序

任务队列

    BlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<>();
    BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(num, true);
    BlockingQueue<Runnable> blockingQueue = new SynchronousQueue<>(true);
  1. LinkedBlockingQueue 推荐用于无界队列,此时容量大小为 Integer.MAX_VALUE。也可指定容量,用作有界队列。
  2. ArrayBlockingQueue 如果为true则在插入或移除时阻塞的线程的队列访问将按 FIFO 顺序处理;如果为false则未指定访问顺序。用于有界队列。
  3. SynchronousQueue 提交的任务不会被保存,总是会马上提交执行。如果为true,则等待线程以 FIFO 顺序竞争访问;否则顺序未指定。无参创建为 false。

任务过多时的拒绝策略

  1. AbortPolicy 任务不能执行或放入队列,抛出异常
  2. CallerRunsPolicy 线程池未关闭时,直接在execute方法的调用线程中运行被拒绝的任务
  3. DiscardOldestPolicy 线程池未关闭时,丢弃队列头部的未处理请求,然后提交任务运行
  4. DiscardPolicy 直接丢弃任务,不做任何处理

可自定义拒绝策略
如下保证任务不会丢失,但可能无法满足业务场景。

        // 线程池未关闭时,在execute方法的调用线程中线程阻塞直到任务放入队列,否则在execute方法的调用线程中运行任务
        RejectedExecutionHandler handler = (runnable, executor) -> {
            if (!executor.isShutdown()) {
                try {
                    executor.getQueue().put(runnable);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    runnable.run();
                }
            } else {
                runnable.run();
            }
        };

java线程池ThreadPoolExecutor类使用详解

线程池提交任务的流程

分 3 个步骤进行:

  1. 如果正在运行的线程少于 corePoolSize,请尝试使用给定命令启动一个新线程作为其第一个任务。对 addWorker 的调用以原子方式检查 runState 和 workerCount,因此通过返回 false 来防止错误警报,这些错误警报会在不应添加线程时添加线程。
  2. 如果一个任务可以成功放入队列,那么我们仍然需要仔细检查我们是否应该添加一个线程(因为自从上次检查后现有的线程已经终止)或者池在进入这个方法后关闭了。因此,我们重新检查状态,如果有必要,如果停止,则回滚排队,或者如果没有,则启动一个新线程。
  3. 如果我们不能排队任务,那么我们尝试添加一个新线程。如果它失败了,我们知道我们已经关闭或饱和,因此拒绝该任务。

JVM 异常处理

未捕获的异常会由 Thread#dispatchUncaughtException 处理
先获取 Thread#UncaughtExceptionHandler,若线程设置了 handler 则调用设置的 handler 处理
否则调用 线程的 ThreadGroup 对象 进行异常处理
ThreadGroup 中 UncaughtExceptionHandler 会找寻顶层父级,父级设置 handler 调用父级处理,否则进行统一的异常打印

详见 Thread#dispatchUncaughtException 方法,未处理的异常 JVM 会自动此方法执行异常处理逻辑。


测试代码源码如下

import java.util.concurrent.*;

public class PoolDemo {
    // java线程池ThreadPoolExecutor类使用详解 https://www.cnblogs.com/dafanjoy/p/9729358.html
    public static void main(String[] args) {
        /*
         * 未捕获的异常会由 Thread#dispatchUncaughtException 处理
         * 先获取 Thread#UncaughtExceptionHandler,若线程设置了 handler 则调用设置的 handler 处理
         * 否则调用 线程的 ThreadGroup 对象 进行异常处理
         * ThreadGroup 中 UncaughtExceptionHandler 会找寻顶层父级,父级设置 handler 调用父级处理,否则进行统一的异常打印
         */
/*
        Thread.currentThread().setUncaughtExceptionHandler(((t, e) -> {
            System.err.println("出错啦");
            e.printStackTrace();
        }));
*/


        ThreadFactory threadFactory = Executors.defaultThreadFactory();
//        ThreadFactory threadFactory = Executors.privilegedThreadFactory();

//        BlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<>();
        BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(1, true);
//        BlockingQueue<Runnable> blockingQueue = new SynchronousQueue<>(true);

        // 线程池未关闭时,在execute方法的调用线程中线程阻塞直到任务放入队列,否则在execute方法的调用线程中运行任务
        RejectedExecutionHandler handler = (runnable, executor) -> {
            if (!executor.isShutdown()) {
                try {
                    executor.getQueue().put(runnable);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    runnable.run();
                }
            } else {
                runnable.run();
            }
        };

        // 任务不能执行或放入队列,抛出异常
//        handler = new ThreadPoolExecutor.AbortPolicy();

        // 线程池未关闭时,直接在execute方法的调用线程中运行被拒绝的任务
//        handler = new ThreadPoolExecutor.CallerRunsPolicy();

        // 线程池未关闭时,丢弃队列头部的未处理请求,然后提交任务运行
//        handler = new ThreadPoolExecutor.DiscardOldestPolicy();

        // 直接丢弃任务,不做任何处理
//        handler = new ThreadPoolExecutor.DiscardPolicy();


        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                1, 1, 30L, TimeUnit.SECONDS,
                blockingQueue, threadFactory, handler);
        threadPoolExecutor.execute(() -> {
            System.out.printf("%s execute0%n", Thread.currentThread().getName());
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        System.out.println("submit0");

        for (int i = 0; i < 2; i++) {
            final int j = i + 1;
            System.out.printf("submit%d%n", j);
//            try {
            threadPoolExecutor.execute(() -> System.out.printf("%s execute%d%n", Thread.currentThread().getName(), j));
//            } catch (Exception e) {
//                System.err.print("Exception in thread \"" + Thread.currentThread().getName() + "\" ");
//                e.printStackTrace(System.err);
//            }
        }
        System.out.println("关闭线程池");
        threadPoolExecutor.shutdown();
    }
}

标签:Java,队列,任务,handler,线程,new,异常,ThreadPoolExecutor
From: https://www.cnblogs.com/pong137/p/17487306.html

相关文章

  • Java值传递
    一、形参&实参实参:实际传递给方法的参数形参:用于定义方法,接收实参,不需要有确定的值 二、值传递&引用传递值传递:方法接受的是实参值的拷贝,会创建副本引用传递:方法接收的是实参所引用对象在堆中的地址,不会创建副本,对形参的修改将影响到形参Java只有值传递  publicstati......
  • JAVA集合
    一、集合框架概览Java集合也叫做容器,由两大接口派生而来,一个是collection接口,主要用于存放单一元素,另一个是map接口,用于存放键值对。collection有三个子接口:list、set、queue。相较于数组,Java集合的优势在于它们的大小可变、支持泛型、具有内建算法,比如add(),remove()等。 l......
  • c++线程安全队列--有锁
    C++线程安全队列是一种数据结构,用于在多线程环境中安全地共享数据。它提供了一组功能,确保多个线程可以同时读取和写入队列,而不会导致竞争条件或数据损坏。C++线程安全队列的常见功能:入队操作(Enqueue):将一个元素添加到队列的尾部。这个操作必须是原子的,以确保在多线程环境中不会......
  • java——微服务——spring cloud——Nacos——Nacos配置共享
       ......
  • java——微服务——spring cloud——Nacos——Nacos配置热更新
                                                          ......
  • Java 命名规范
    包命名规范包(Package)的作用是将功能相似或相关的类或者接口进行分组管理,便于类的定位和查找,同时也可以使用包来避免类名的冲突和访问控制,使代码更容易维护。通常,包名使用小写英文字母进行命名,并使用“.”进行分割,每个被分割的单元只能包含一个名词。一般地,包命名常采用顶级域......
  • 【Java技术专题】「Guava开发指南」手把手教你如何进行使用Guava工具箱进行开发系统实
    异常传播有时候,您可能需要重新抛出捕获到的异常。这种情况通常发生在捕获到Error或RuntimeException时,因为您可能没有预料到这些异常,但在声明捕获Throwable和Exception时,它们也被包含在内了。为了解决这个问题,Guava提供了多种方法来判断异常类型并重新抛出异常。例如:try{......
  • JavaScript & TypeScript 学习总结
    @目录JavaScriptJavaScriptBOM对象JavaScriptDocument对象JavaScript事件处理JavaScript变量JavaScript函数基础JavaScript流程控制JavaScript数据类型JavaScript数组JavaScript运算符JavaScript正则表达式JavaScript字符串函数TypeScript简单示例JavaScriptJavaScriptBOM对......
  • SpringBoot整合JavaMail
    第一步:第二步: 第三步:第四步: ......
  • Java分布式框架之Dubbo
    分布式与微服务 1、传统架构当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是影响项目开发的关键。存在的问题:代码耦合,开发维护困难无法对不同模块进行针对性优化无法水平扩展单点容错率低,并......