关于Java线程池核心内容

本文介绍了线程池的优势,包括降低线程创建成本、提高响应速度和资源利用率。重点讲解了在多IO场景下的应用,以及线程池中的重要参数如核心线程数、最大线程数等。还对比了execute和submit两种任务提交方式,并讨论了线程池的关闭和中断机制。
摘要由CSDN通过智能技术生成

线程池的优势

首先,线程池是将多个线程进行池化操作,统一进行管理,这样做有什么好处呢?

  • 降低创建、销毁线程的开销线程池中维护固定数量的线程,不需要临时进行线程的创建和销毁

  • 提高响应速度 对于新提交到线程池中的任务,直接使用线程池中的空闲线程可以直接进行处理,不需要等待创建线程

  • 节省资源可以重复利用线程。

什么场景下会用到线程池?

一般就是多 IO 的场景下需要用到,像 IO 任务很多,比如数据库操作、请求其他接口操作,这都属于 IO 类任务,IO 类任务的特点就是只需要线程去启动一下 IO 任务,之后就等待 IO 结果返回即可,IO 结果返回的时间是比较慢的 ,因此如果只使用单线程去执行 IO 任务的话,由于这个等待时间比较长,那么线程需要一直等待 IO 结果返回,而无法执行其他操作。

因此在多 IO 场景下,可以使用线程池来加快 IO 任务的执行,开启多个线程同时去启动多个 IO 任务,可以加快 IO 任务的处理速度。

线程池中重要的参数

线程池中重要的参数如下:

  • corePoolSize :核心线程数量

  • maximumPoolSize :线程池最大线程数量 = 核心线程数+非核心线程数

  • keepAliveTime :非核心线程存活时间

  • unit:空闲线程存活时间单位(keepAliveTime单位)

  • workQueue :工作队列(任务队列),存放等待执行的任务

    • LinkedBlockingQueue:无界的阻塞队列,最大长度为 Integer.MAX_VALUE

    • ArrayBlockingQueue:基于数组的有界阻塞队列,按FIFO排序

    • SynchronousQueue:同步队列,不存储元素,对于提交的任务,如果有空闲线程,则使用空闲线程来处理;否则新建一个线程来处理任务

    • PriorityBlockingQueue:具有优先级的无界阻塞队列,优先级通过参数Comparator实现。

  • threadFactory :线程工厂,创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等。

  • handler:拒绝策略 ,有4种(也可以自定义拒绝策略)

    • AbortPolicy :直接抛出异常,默认策略

    • CallerRunsPolicy:用调用者所在的线程来执行任务

    • DiscardOldestPolicy:丢弃阻塞队列里最老的任务,也就是队列里靠前的任务

    • DiscardPolicy :当前任务直接丢弃

对于新加入的任务,线程池如何进行处理?

对于新加入一个任务,线程池处理流程如下:

  1. 如果核心线程数量未达到,创建核心线程执行

  2. 如果当前运行线程数量已经达到核心线程数量,查看任务队列是否已满

  3. 如果任务队列未满,将任务放到任务队列

  4. 如果任务队列已满,看最大线程数是否达到,如果未达到,就新建非核心线程处理

  5. 如果当前运行线程数量未达到最大线程数,则创建非核心线程执行

  6. 如果当前运行线程数量达到最大线程数,根据拒绝策略处理。

如何将任务提交到线程池中?

有两种方式:execute submit

这两种方式的区别:

  • execute

    • execute 没有返回值

    • execute 无法捕获任务过程中的异常

  • submit

    • submit 会返回一个 Future 对象,用来获取任务的执行结果

    • submit 可以通过 Future 对象来捕获任务中的异常

execute 方式如下:

ExecutorService executor = Executors.newFixedThreadPool(5);
executor.execute(new Runnable() {
   public void run() {
       // 执行具体的任务逻辑
       System.out.println("Task executed using execute method");
  }
});
executor.shutdown();

submit 方式如下:

ExecutorService executor = Executors.newFixedThreadPool(5);
Future<String> future = executor.submit(new Callable<String>() {
   public String call() {
       // 执行具体的任务逻辑
       return "Task executed using submit method";
  }
});

try {
   String result = future.get(); // 获取任务执行结果
   System.out.println(result);
} catch (InterruptedException e) {
   // 处理中断异常
} catch (ExecutionException e) {
   // 处理任务执行异常
} finally {
 // 关闭线程池
 executor.shutdown();
}

线程池是如何关闭的?

通过调用线程池的 shutdown() 方法即可关闭线程池

调用之后,会设置一个标志位表示当前线程池已经关闭,会禁止向线程池中提交新的任务

去中断所有的空闲线程并且等待正在执行的任务执行完毕(通过调用线程 interrupt() 方法),当线程池中所有任务都执行完毕之后,线程池就会被完全关闭。

扩展:thread.interrupt() 方法调用后线程不会立即中断。调用 interrupt 只是将被中断线程的中断状态设置为 true,通知被中断的线程自己处理中断,而不是立即强制的让线程直接中断(强制中断不安全)

当外部调用线程进行中断的命令时,如果该线程处于被阻塞的状态,如 Thread.sleep(),Object.wait(),BlockingQueue#put,BlockingQueue#take 等等时,那么此时调用该线程的 interrupt 方法就会抛出 InterruptedException 异常

因此,可以通过这个特点来优雅的停止线程(在 《Java多线程核心技术》 一书中说到):将 sleep() 和 interrupt() 搭配使用,来停止线程。

  • 23
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值