线程池介绍

线程池的构造方法
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) 
参数介绍
  • corePoolSize(线程池基本大小):当向线程池提交任务时,如果线程池中现有的线程数小于corePoolSize,即便此时由空闲线程,也会通过创建一个新的线程来执行该任务,直到当前线程数大于等于corePoolSize。

  • maximumPoolSize(线程池最大线程数):当线程阻塞队列满了,且创建的线程数小于maximumPoolSize,则线程池会创建新的线程来执行任务。

  • keepAliveTime(线程存活时间):当非核心线程空闲时间超过了keepAliveTime,那么这个线程就会被销毁。直到线程池中的线程数等于核心线程数。unit是时间单位。

  • workQueue(任务队列):用于传输和保存等待执行任务的阻塞队列。

  • threadFactory(线程工厂):用于创建新线程,对创建的线程名可使用统一命名标准。

  • handler(线程饱和策略):当线程池和队列都满了,新加入的线程会执行该策略。

线程池流程

submit和execute方法

在Java的线程池编程中,submitexecute是两种提交任务的方法,它们有一些关键的区别。

方法的来源

  • execute方法定义在Executor接口中,是线程池的基础方法,用于执行Runnable任务。

  • submit方法则定义在ExecutorService接口中,提供了更多的灵活性,可以提交实现了CallableRunnable接口的任务。

接收的参数

  • execute只能接收Runnable类型的参数。

  • submit可以接收RunnableCallable两种类型的参数,这使得submit可以处理有返回值的任务。

返回值

  • execute方法没有返回值。

  • submit方法返回一个Future对象,可以通过这个对象获取任务执行的结果。如果提交的是Runnable任务,Future.get()将返回null,因为Runnable任务本身不返回值。

异常处理

  • 使用execute提交的任务,如果在run()方法中出现异常,会直接在控制台打印异常信息。

  • 使用submit提交的任务,如果在run()call()方法中出现异常,异常信息不会立即打印。只有在调用Future.get()方法时,才会抛出异常。因此,当使用submit时,建议显式地捕获和处理异常,以避免丢失异常信息。

/**
 * 该方法接收Runnable任务,没有返回值
 */
public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    execute(ftask);
    return ftask;
}

/**
 * 会将线程执行结果赋值给result
 */
public <T> Future<T> submit(Runnable task, T result) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task, result);
    execute(ftask);
    return ftask;
}

/**
 * 返回线程执行结果
 */
public <T> Future<T> submit(Callable<T> task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task);
    execute(ftask);
    return ftask;
}

创建线程池的静态类 Executors

Executors类提供了4种不同的线程池

  1. newCachedThreadPool:用来创建一个可以无限扩大的线程池,适用于负载较轻的场景,多用来执行短期异步任务,不会造成cpu过度切换。

  2. newFixedThreadPool:创建一个固定大小的线程池,采用无界的阻塞队列,所以线程数量永远不会变化,Integer.MAX_VALUE大小。适用于负载较重的场景,对当前线程数量进行限制。(保证线程数可控,不会造成线程过多,导致系统负载更为严重)

  3. newSingleThreadExecutor:创建一个单线程的线程池,适用于需要保证顺序执行各个任务。与自己创建一个线程的区别:自己创建的线程抛出异常后,线程不会恢复,线程池的线程在异常后,会自动创建一个新的线程。

  4. newScheduledThreadPool:适用于执行延时或者周期性任务。