Java 线程池学习

线程创建的3种方式

继承 Thread 类

package ThreadPool;

public class ThreadExample extends Thread{
  // 重写 run 方法
  @Override
  public void run() {
    for (int i = 0; i < 100; i++) {
      System.out.println("Thread:: " + i);
    }
  }

  public static void main(String[] args) {
    ThreadExample thread = new ThreadExample();
    thread.start();
  }
}
  • run() 方法是线程的具体逻辑操作
  • start() 方法调用一个 native start0() 方法,通过操作系统启动一个线程

实现 Runnable 接口

package ThreadPool;

import java.util.Date;

public class RunnableExample implements Runnable{
  private String command;

  public RunnableExample(String command) {
    this.command = command;
  }

  @Override
  public void run() {
    // TODO Auto-generated method stub
    System.out.println(Thread.currentThread().getName() + " Start Time: " + new Date());
    this.process();
    System.out.println(Thread.currentThread().getName() + " End Time: " + new Date());
  }

  public void process() {
    try {
      Thread.sleep(5000);
    }
    catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
  
  public static void main(String[] args) {
    Runnable runnable = new RunnableExample("Runnable Thread");
    Thread thread = new Thread(runnable);
    thread.start();
  }
}
  • 重写 run() 方法
  • 创建 Thread 类实例,传入 Runnable 参数
  • 通过 start() 方法启动线程

实现 Callable 接口

public class CallableExample implements Callable<String>{
  private String name;
  public CallableExample(String name) {
    this.name = name;
  }

  @Override
  public String call() throws Exception {
    // TODO Auto-generated method stub
    return this.name;
  }

  public static void main(String[] args) {
    Callable callable = new CallableExample("Callable Thread");
    FutureTask<String> futureTask = new FutureTask<>(callable);

    for (int i = 0; i < 100; i++) {
      System.out.println(Thread.currentThread().getName() + ": " + i);
      if (i == 20) {
        new Thread(futureTask, "ThreadName").start();
      }
    }

    try {
      System.out.println("Return: " + futureTask.get());
    }
    catch(InterruptedException e) {
      e.printStackTrace();
    }
    catch (ExecutionException e) {
      e.printStackTrace();
    }
  }
}
  • 重写 call() 方法
  • 创建 FutureTask 实例异步接收线程返回结果
  • 创建 Thread 实例,传入 FutureTask 参数
  • 打印返回值

Runnable 与 Callable 接口

@FunctionalInterface
public interface Runnable {
  public abstract void run();
}

@FunctionalInterface
public interface Callable<V> {
  V call() throws Exception;
}
  • Runnable自JDK1.0开始就一直存在,Callable是JDK1.5引入的
  • Runnable 不会返回结果和抛出异常,Callable可以返回结果和抛出异常
  • Executors 可以将 Runnable 对象转换为 Callable 对象
public class Executors {
  public static <T> Callable<T> callable(Runnable task, T result) {
    if (task == null)
      throw new NullPointerException();
    return new RunnableAdapter<T>(task, result);
  }
  
  public static Callable<Object> callable(Runnable task) {
    if (task == null)
      throw new NullPointerException();
    return new RunnableAdapter<Object>(task, null);
  }
  
  static final class RunnableAdapter<T> implements Callable<T> {
    final Runnable task;
    final T result;
    RunnableAdapter(Runnable task, T result) {
      this.task = task;
      this.result = result;
    }
    public T call() {
      task.run();
      return result;
    }
  }
}

线程池技术

线程池提供了一种限制和管理资源的方式,每个线程池也维护一些基本信息。

使用线程池的好处:

  • 降低资源消耗: 重复利用已创建的线程降低线程创建和销毁造成的消耗
  • 提高响应速度: 当任务到达时,不需要等待线程的创建就能立即执行
  • 提高线程的可管理性: 线程是稀缺资源,使用线程池可以进行统一的分配、调优和监控

Executor 框架

使用 Executor 来启动线程比 Thread.start() 更好,除了更易管理,效率更高外,还能避免 this 逃逸问题。

/**
 * this 逃逸问题
 * 在构造函数返回之前,其它线程就持有该对象的引用,调用尚未构造完全的对象的方法
 */
public class ThisEscape {
  final String a;
  static ThisEscape t;
  
  public ThisEscape() {
    t = this;
    try {
      Thread.sleep(200);
    }
    catch(InterruptedException e) {
      e.printStackTrace();
    }
    a = "Lzy";
  }
  
  public static void main(String[] args) {
    Thread t1 = new Thread(() -> {
      new ThisEscape();
    });
    
    Thread t2 = new Thread(() -> {
      System.out.printLn(ThisEscape.t.a);    // 输出null, ThisEscape还未构造完成
    });
    
    t1.start();
    t2.start();
  }
}

Executor 框架构成

  • 任务(Runnable/Callable)
  • 任务的执行(Executor)
  • 异步计算的结果(Future): 调用 submit() 方法执行返回 FutureTask 对象

ThreadPoolExecutor 类

// ctl: 保存线程池的状态,原子整数
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;    //29
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

// 线程池状态,存储在高位
private static final int RUNNING    = -1 << COUNT_BITS;
private static final int SHUTDOWN   =  0 << COUNT_BITS;
private static final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;
/**
 * runStateOf: 返回线程池状态
 * workerCountOf: 返回线程池内有效线程数量
 */
private static int runStateOf(int c)     { return c & ~CAPACITY; }
private static int workerCountOf(int c)  { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }

// 任务队列
private final BlockingQueue<Runnable> workQueue;

/**
 * ThreadPoolExecutor 构造方法
 * 
 * @param corePoolSize: 线程池的核心线程数(即池中保留的线程数,即使线程处于空闲状态)
 * @param maximumPoolSize: 线程池的最大线程数
 * @param keepAliveTime: 当线程数大于 corePoolSize 时,多余的空闲线程最大存活时间
 * @param unit: keepAliveTime 的时间单位
 * @param workQueue: 任务队列,存储待执行的任务(只保存 Runnable 任务)
 * @param threadFactory: 线程工程,用于创建新的线程
 * @param handler: 拒绝策略
 */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
  if (corePoolSize < 0 ||
      maximumPoolSize <= 0 ||
      maximumPoolSize < corePoolSize ||
      keepAliveTime < 0)
    throw new IllegalArgumentException();
  if (workQueue == null || threadFactory == null || handler == null)
    throw new NullPointerException();
  this.corePoolSize = corePoolSize;
  this.maximumPoolSize = maximumPoolSize;
  this.workQueue = workQueue;
  this.keepAliveTime = unit.toNanos(keepAliveTime);
  this.threadFactory = threadFactory;
  this.handler = handler;
}

/**
 * 拒绝策略
 * 1. AbortPolicy: 抛出 RejectedExecutionException 异常
 * 2. CallerRunsPolicy: 在调用者的线程执行任务r, 如果执行程序被关闭, 则丢弃该任务
 * 3. DiscardPolicy: 直接丢弃任务, 不做任何处理
 * 4. DiscardOldestPolicy: 丢弃最早未被处理的任务
 */
public static class AbortPolicy implements RejectedExecutionHandler {
  public AbortPolicy() { }
  public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString());
  }
}

public static class CallerRunsPolicy implements RejectedExecutionHandler {
  public CallerRunsPolicy() { }
  public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    if (!e.isShutdown()) {
      r.run();
    }
  }
}

public static class DiscardPolicy implements RejectedExecutionHandler {
  public DiscardPolicy() { }
  public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
  }
}

public static class DiscardOldestPolicy implements RejectedExecutionHandler {
  public DiscardOldestPolicy() { }
  public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    if (!e.isShutdown()) {
      // workQueue.poll();
      e.getQueue().poll();
      e.execute(r);
    }
  }
}

线程池使用案例

Runnable 接口实现类

package thread_pool;

import java.util.Date;

public class RunnableExample implements Runnable {
  private String command;

  public RunnableExample(String command) {
    this.command = command;
  }

  public String getCommand() {
    return this.command;
  }

  public void setCommand(String command) {
    this.command = command;
  }

  @Override
  public void run() {
    // TODO Auto-generated method stub
    System.out.println(Thread.currentThread().getName() + " Start Time: " + new Date());
    process();
    System.out.println(Thread.currentThread().getName() + " End Time: " + new Date());
  }

  public void process() {
    try {
      Thread.sleep(5000);
    }
    catch(InterruptedException e) {
      e.printStackTrace();
    }
  }

}

ThreadPoolExecutorExample类

package thread_pool;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolExecutorExample {
  private static final int CORE_POOL_SIZE = 5;
  private static final int MAX_POOL_SIZE = 10;
  private static final int QUEUE_CAPACITY = 100;    // 任务队列容量
  private static final long KEEP_ALIVE_TIME = 1L;

  public static void main(String[] args) {
    ThreadPoolExecutor executor = new ThreadPoolExecutor
      (CORE_POOL_SIZE, 
       MAX_POOL_SIZE, 
       KEEP_ALIVE_TIME, 
       TimeUnit.SECONDS,
       new ArrayBlockingQueue<>(QUEUE_CAPACITY),
       new ThreadPoolExecutor.CallerRunsPolicy());

    // 创建 RunnableExample 实例
    for (int i = 0; i < 10; i++) {
      Runnable worker = new RunnableExample(i + "");
      executor.execute(worker);
    }

    executor.shutdown();
    // 如果 excutor 未结束,将进入死循环(验证线程池已关闭)
    while (!executor.isTerminated()) {}
    System.out.println("Finshed all threads");
  }
}

线程池原理

excutor.execute(worker)

public void execute(Runnable command) {
  if (command == null) {
    throw new NullPointerException();
  }
  
  int c = ctl.get();
  
  /**
   * 判断当前线程池中线程数量是否小于 corePoolSize
   * 如果小于,则通过 addWorker 创建一个新的线程,并将任务 command 添加到该线程
   */
  if (workerCountOf(c) < corePoolSize) {
    if (addWorker(command, true))    // true 表示线程上限为 corePoolSize
      return;
    c = ctl.get();
  }
  
  /**
   * 如果线程数量 >= corePoolSize, 则判断线程池的状态
   * 如果线程池处于 RUNNING 状态,且队列可以加入任务
   */
  if (isRunning(c) && workQueue.offer(command)) {
    // 再次获取线程池状态
    int recheck = ctl.get();
    
    // 如果线程池不是 RUNNING 状态,需要从任务队列中移除任务,并尝试判断线程是否全部执行完毕
    if (! isRunning(recheck) && remove(command))
      reject(command);    // 对任务执行拒绝策略
    else if (workerCountOf(recheck) == 0)
      addWorker(null, false);
  }
  
  /**
   * 如果任务不能加入队列,则通过 addWorker 创建新线程
   * 如果线程创建失败,则对任务执行拒绝策略
   */
  else if (!addWorker(command, false))    // false表示线程上限为 maxPoolSize
    reject(command);
}

execute() 与 submit() 方法

// submit 方法, execute 方法如上所示
public abstract class AbstractExecutorService implements ExecutorService { 
  public <T> Future<T> submit(Callable<T> task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task);
    execute(ftask);
    return ftask;
  }
  
  protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
    return new FutureTask<T>(callable);
  }
}
  • execute() 用于提交不需要返回值的任务,无法判断任务是否被线程执行成功
  • submit() 用于提交需要返回值的任务,线程池会返回一个 Future 类型的对象
  • Future 对象的 get() 方法可以获取返回值
public interface Future<V> {
  /**
   * 尝试取消任务执行
   * @return false: 由于任务已经正常执行完成, 任务没有被中断
   * @return true: 其它情况
   */
  boolean cancel(boolean mayInterruptIfRunning);
  
  /**
   * @return true: 任务在正常执行完成之前被取消
   * @return false: 任务正常执行完成
   */
  boolean isCancelled();
  
  /**
   * 任务是否完成
   * @return true: 正常结束、异常、任务取消都返回 true
   * @return false:
   */
  boolean isDone();
  
  /**
   * 等待获取执行结果
   */
  V get() throws InterruptedException, ExecutionException;
  
  /**
   * 等待获取执行结果
   * @param timeout: 最大等待时长
   * @param unit: timeout单位
   */
  V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}

shutdown() 与 shutdownNow()

shutdown(): 关闭线程池,线程池不再接受新的任务,但是等待队列的任务会执行完毕。

shutdownNow(): 关闭线程池,线程池会终止当前正在运行的任务,停止处理等待队列的任务并返回List。

public class ThreadPoolExecutor extends AbstractExecutorService {
  public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
      checkShutdownAccess();
      advanceRunState(SHUTDOWN); // 线程池状态变为 SHUTDOWN
      interruptIdleWorkers();    // 中断空闲线程
      onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
      mainLock.unlock();
    }
    tryTerminate();
  }
  
  public List<Runnable> shutdownNow() {
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
      checkShutdownAccess();
      advanceRunState(STOP); // 线程池状态变为 STOP
      interruptWorkers();    // 中断所有线程
      tasks = drainQueue();  // 返回待执行任务列表
    } finally {
      mainLock.unlock();
    }
    tryTerminate();
    return tasks;
  }
}

Executors 工具类创建线程池

FixedThreadPool

public static ExecutorService newFixedThreadPool(int nThreads) {
  return new ThreadPoolExecutor(nThreads, nThreads,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>());
}

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
  return new ThreadPoolExecutor(nThreads, nThreads,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>(),
                                threadFactory);
}

可重用固定线程数的线程池,核心线程数 = 最大线程数

FixedThreadPool 使用无界队列作为线程池的工作队列

  • public LinkedBlockingQueue() {
      this(Integer.MAX_VALUE);
    }
    
  • 运行中的 FixedThreadPool 线程池不会拒绝任务,在任务比较多时会导致OOM溢出

SingleThreadExecutor

public static ExecutorService newSingleThreadExecutor() {
  return new FinalizableDelegatedExecutorService
    (new ThreadPoolExecutor(1, 1,
                            0L, TimeUnit.MILLISECONDS,
                            new LinkedBlockingQueue<Runnable>()));
}

public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
  return new FinalizableDelegatedExecutorService
    (new ThreadPoolExecutor(1, 1,
                            0L, TimeUnit.MILLISECONDS,
                            new LinkedBlockingQueue<Runnable>(),
                            threadFactory));
}

SingleThreadExecutor 只有一个线程的线程池,核心线程数 = 最大线程数 = 1。

SingleThreadExecutor 也使用无界队列作为线程池的工作队列,与 FixedThreadPool 相同也会导致内存溢出问题。

CachedThreadPool

public static ExecutorService newCachedThreadPool() {
  return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                60L, TimeUnit.SECONDS,
                                new SynchronousQueue<Runnable>());
}

public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
  return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                60L, TimeUnit.SECONDS,
                                new SynchronousQueue<Runnable>(),
                                threadFactory);
}

CachedThreadPool 根据需要创建新的线程,核心线程数 = 0,最大线程数 = Integer.MAX_VALUE

CachedThreadPool 的线程数是无界的,即主线程提交任务的速度大于线程池中线程处理任务的速度,则会不断有新的线程创建,可能导致 cpu资源和内存资源耗尽。

ScheduledThreadPoolExecutor

public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService {
  
  // 固定周期执行
  public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                long initialDelay,
                                                long period,
                                                TimeUnit unit) {
    if (command == null || unit == null)
      throw new NullPointerException();
    if (period <= 0)
      throw new IllegalArgumentException();
    ScheduledFutureTask<Void> sft =
      new ScheduledFutureTask<Void>(command,
                                    null,
                                    triggerTime(initialDelay, unit),
                                    unit.toNanos(period));
    RunnableScheduledFuture<Void> t = decorateTask(command, sft);
    sft.outerTask = t;
    delayedExecute(t);    //super.getQueue().add(t); 
    return t;
  }
  
  // 固定延时执行
  public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                   long initialDelay,
                                                   long delay,
                                                   TimeUnit unit) {
    if (command == null || unit == null)
      throw new NullPointerException();
    if (delay <= 0)
      throw new IllegalArgumentException();
    ScheduledFutureTask<Void> sft =
      new ScheduledFutureTask<Void>(command,
                                    null,
                                    triggerTime(initialDelay, unit),
                                    unit.toNanos(-delay));
    RunnableScheduledFuture<Void> t = decorateTask(command, sft);
    sft.outerTask = t;
    delayedExecute(t);  
    return t;
  }
}
  • 当调用 scheduleAtFixedRate()scheduleWithFixedDelay() 方法时,向线程池的 DelayQueue 添加一个 RunnableScheduledFuture 实例。
  • 线程池从线程从 DelayQueue 中获取 RunnableScheduledFuture 任务执行。

DelayQueue

public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService {
  private static final int INITIAL_CAPACITY = 16;
  private RunnableScheduledFuture<?>[] queue =
    new RunnableScheduledFuture<?>[INITIAL_CAPACITY];
  private final ReentrantLock lock = new ReentrantLock();
  private int size = 0;
  
  // 指定在队列头部等待任务的线程
  private Thread leader = null;
  
  // 表示一个新的任务在队列头部变得可用 或 一个新的线程需要成为 leader
  private final Condition available = lock.newCondition();
}

DelayQueue 本质是一个优先级队列,任务延时时间短的放在前面先被执行,如果延时时间相同则先提交的任务先被执行。

线程池大小设置

  • CPU密集型任务(N+1):N为CPU核心数
  • I/O密集型任务(2N)

参考文章

[1] Java线程池详解

[2] this引用逃逸问题

posted @ 2022-07-02 04:01  ylyzty  阅读(27)  评论(0编辑  收藏  举报