线程池详解

目录

线程池简介

线程池的使用

ThreadPoolExecutor—推荐使用

Executors—不推荐使用

提交任务

如何执行批量任务

如何执行定时、延时任务

如何执行周期、重复性任务

关闭线程池

线程池的参数设计分析

核心线程数(corePoolSize)

任务队列长度(workQueue)

最大线程数(maximumPoolSize)

最大空闲时间(keepAliveTime)

线程池原理分析

线程池执行任务的具体流程是怎样的?

线程池的五种状态是如何流转的?

线程池中的线程是如何关闭的?

线程池为什么一定得是阻塞队列?

线程发生异常,会被移出线程池吗?

线程池源码分析

线程池源码的基础属性和方法

execute方法

addWorker方法

runWorker方法

processWorkerExit方法

getTask方法

shutdown方法

shutdownNow方法

mainLock


线程池简介

线程池(Thread Pool)是一种基于池化思想管理线程的工具,经常出现在多线程服务器中,如Tomcat。

线程过多会带来额外的开销,其中包括创建销毁线程的开销、调度线程的开销等等,同时也降低了计算机的整体性能。线程池维护多个线程,等待监督管理者分配可并发执行的任务。这种做法,一方面避免了处理任务时创建销毁线程开销的代价,另一方面避免了线程数量膨胀导致的过分调度问题,保证了对内核的充分利用。

线程池有如下的优势:

  • 降低资源消耗:通过池化技术重复利用已创建的线程,降低线程创建和销毁造成的损耗。
  • 提高响应速度:任务到达时,无需等待线程创建即可立即执行。
  • 提高线程的可管理性:线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会因为线程的不合理分布导致资源调度失衡,降低系统的稳定性。使用线程池可以进行统一的分配、调优和监控。
  • 提供更多更强大的功能:线程池具备可拓展性,允许开发人员向其中增加更多的功能。比如延时定时线程池ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行。

线程池的使用

创建线程池

ThreadPoolExecutor—推荐使用

利用ThreadPoolExecutor提供的构造方法创建线程池

 主要看参数最全的构造方法,其他构造方法最终还是会调用该改造方法

线程池的核心参数

corePoolSize:核心线程数,线程池初始化时默认是没有线程的,当任务来临时才开始创建线程去执行任务。

maximumPoolSize:最大线程数,当核心线程数已满,且队列已满时,如果池子里的工作线程数小于maximumPoolSize,则会创建非核心线程执行任务。

keepAliveTime:非核心线程数的空闲时间超过keepAliveTime就会被自动终止回收掉,但在corePoolSize=maximumPoolSize时,该值无效,因为不存在非核心线程。

unit:keepAliveTime的时间单位。

workQueue:用于保存线程任务的队列,主要分为无界、有界、同步移交等队列,当池子里的工作线程数大于corePoolSize,就会将新进来的线程任务放入队列中。

  • ArrayBlockingQueue(有界队列):队列长度有限,当队列满了就需要创建非核心线程执行任务,如果最大线程数已满,则执行拒绝策略 
  • LinkedBlockingQueue(无界队列):队列长度无限,当任务处理速度跟不上任务创建速度,可能会导致内存占用过多或OOM
  • SynchronousQueue(同步队列):队列不作为任务的缓冲处理,队列长度为0。

threadFactory:

  • 创建线程的工厂接口,默认使用Executors.defaultThreadFactory()
  • 另外可以实现ThreadFactory接口,自定义线程工厂

handler:线程池无法继续接收任务时(workQueue已满和maximumPoolSize已满)的拒绝策略

  • AbortPolicy:默认拒绝策略,中断抛出RejectedExecutionException异常
  • CallerRunsPolicy:让提交任务的主线程来执行任务
  • DiscardOldestPolicy:丢弃在队列中存在时间最久的任务,重复执行
  • DiscardPolicy:丢弃任务,不进行任何通知
  • 另外可以实现RejectedExecutionHandler接口,自定义拒绝策略

以下是一个使用ThreadPoolExecutor创建线程池的示例:

import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 自定义实现线程工厂
 */
public class CustomThreadFactory implements ThreadFactory {
  private final AtomicInteger i = new AtomicInteger(1);

  @Override
  public Thread newThread(Runnable r) {
    // 创建线程
    Thread thread = new Thread(r);
    // 设置线程名称
    thread.setName("线程" + i.getAndIncrement());
    return thread;
  }
}
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.*;

/**
 * 线程池例子
 */
@Slf4j
public class ThreadPoolExample2 {

  public static void main(String[] args) {
    // 线程池的核心线程数
    int corePoolSize = 3;
    // 线程池的最大线程数
    int maximumPoolSize = 5;
    // 线程池的任务队列
    ArrayBlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(3);
    // 线程池中工作线程(默认是非核心线程)保持空闲的时间
    long keepAliveTime = 60L;
    // 时间单位
    TimeUnit unit = TimeUnit.SECONDS;
    // 自定义线程工厂,不写会使用默认工厂
    ThreadFactory threadFactory = new CustomThreadFactory();
    // 线程池的拒绝策略
    RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
    // 创建线程池
    ThreadPoolExecutor executor = new ThreadPoolExecutor(
        corePoolSize,
        maximumPoolSize,
        keepAliveTime,
        unit,
        workQueue,
        threadFactory,
        handler
    );

    // 提交任务到线程池,最大线程5,队列3,所以最大支持8个线程,大于8会抛出异常,
    // 核心线程3,队列3,所以当线程为6的时候只会开3个线程,超过6才会开启新的线程,直到达到最大线程5
    for (int i = 0; i < 8; i++) {
      final int taskId = i;
      executor.execute(() -> {
        try {
          // 模拟业务处理时间
          Thread.sleep(2000);
        } catch (InterruptedException e) {
          throw new RuntimeException(e);
        }
        log.info("Task " + taskId + " is running by " + Thread.currentThread().getName());
      });
    }

    // 关闭线程池,不再接受新任务
    executor.shutdown();
    // 关闭线程池后再提交新任务会报错
//    executor.execute(new Runnable() {
//      @Override
//      public void run() {
//        System.out.println("新任务");
//      }
//    });
    try {
      // 等待所有任务完成,阻塞当前线程,直到所有已提交的任务执行完毕,或者达到指定的等待时间
      if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
        // 如果超时后任务仍未完成,则强制关闭线程池
        executor.shutdownNow();
      }
    } catch (InterruptedException e) {
      // 如果等待过程中被中断,也强制关闭线程池
      executor.shutdownNow();
    }
    System.out.println("All tasks are done or interrupted.");
  }
}

Executors—不推荐使用

Executors提供了各种线程池类型创建的静态方法,如常见的newFixedThreadPool、newSingleThreadExecutor、newCachedThreadPool、newSingleThreadScheduledExecutor。

以下是一个使用Java中的ExecutorService和Executors创建线程池的简单示例:

import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 线程池例子
 */
@Slf4j
public class ThreadPoolExample {

  public static void main(String[] args) {
    // 创建一个固定大小的线程池
    ExecutorService executor = Executors.newFixedThreadPool(5);
    // 提交任务到线程池
    for (int i = 0; i < 10; i++) {
      final int taskId = i;
      executor.execute(() -> {
        try {
          Thread.sleep(2000);
        } catch (InterruptedException e) {
          throw new RuntimeException(e);
        }
        log.info("Task " + taskId + " is running by " + Thread.currentThread().getName());
      });
    }
    // 关闭线程池
    executor.shutdown();
    while (!executor.isTerminated()) {
      // 等待所有任务完成
    }
    System.out.println("All tasks are done.");
  }
}

但不提倡使用该种方式创建线程池,在阿里巴巴JAVA开发手册,对于线程池创建要求:

提交任务

线程池提交任务有两种方法:

  • 无返回值的任务使用 public void execute(Runnable command) 方法提交;
  • 有返回值的任务使用:
    • Future submit(Runnable task) : 提交Runnable任务
    • Future submit(Runnable task, T result): 提交Runnable任务并指定执行结果
    • Future submit(Callable task) : 提交Callable任务

代码示例

import java.util.concurrent.*;

public class ThreadDemo {

  public static void main(String[] args) throws ExecutionException, InterruptedException {
    // 创建任务
    Task task1 = new Task();
    Task task2 = new Task();
    Task task3 = new Task();

    // 创建一个只要一个线程的线程池
    ExecutorService threadpool = Executors.newSingleThreadExecutor();
    threadpool.submit(task1);
    threadpool.submit(task2);
    Future<String> future = threadpool.submit(task3, "执行完成");
    System.out.println("future:" + future.get());

    Callable callable = new Callable() {
      @Override
      public Object call() throws Exception {
        return "执行完成";
      }
    };
    Future future2 = threadpool.submit(callable);
    System.out.println("future2:" + future2.get());
  }
}

class Task implements Runnable {

  @Override
  public void run() {
    try {
      Thread.sleep(2000);
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    }
    System.out.println(Thread.currentThread().getName());
  }
}

如何执行批量任务

# 执行批量任务,返回它们的执行结果
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException
# 执行批量任务,返回指定时间内完成的执行结果,取消未完成的任务
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit)
# 执行批量任务,返回最先完成的执行结果
public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException
# 执行批量任务,返回指定时间内最先完成的执行结果,取消未完成的任务
public <T> T invokeAny(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit)

代码示例

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * 线程池批量提交任务示例
 */
public class InvokeAllDemo {

  public static void main(String[] args) {
    List<Task> tasks = new ArrayList<>();
    for (int i = 1; i <= 10; i++) {
      tasks.add(new Task(i));
    }
    // 创建线程池
    ExecutorService threadpool = Executors.newFixedThreadPool(5);
    try {
      // 批量提交任务
      List<Future<Integer>> futures = threadpool.invokeAll(tasks);
      for (Future<Integer> future : futures) {
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值