多线程(七)-- 线程池(一)

110 阅读3分钟

「这是我参与11月更文挑战的第21天,活动详情查看:2021最后一次更文挑战

1. 为什么要用线程池

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

2. 常用方法

Executors类提供4个静态工厂方法:newCachedThreadPool()、newFixedThreadPool(int)、newSingleThreadExecutor 和 newScheduledThreadPool() ,这些方法最终都是通过ThreadPoolExecutor类来完成的

(1) newCachedThreadPool

  • 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程
  • 它比较适合处理执行时间比较小的任务
  • corePoolSize为0,maximumPoolSize为无限大,意味着线程数量可以无限大
  • keepAliveTime为60S,意味着线程空闲时间超过60S就会被杀死
  • 采用SynchronousQueue装等待的任务,这个阻塞队列没有存储空间,这意味着只要有请求到来,就必须要找到一条工作线程处理他,如果当前没有空闲的线程,那么就会再创建一条新的线程
public static ExecutorService newCachedThreadPool() {
  return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
}

(2) newFixedThreadPool

  • 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
  • corePoolSize 和 maximunPoolSize都为用户设定的线程数量nThreads , 因此maximumPoolSize和keepAliveTime将无效
  • keepAliveTime为0,意味着一旦有多余的空闲线程,就会被立即停止掉;但这里keepAliveTime无效
  • 阻塞队列采用了LinkedBlockingQueue,它是一个无界队列,因此永远不可能拒绝任务
public static ExecutorService newFixedThreadPool(int nThreads) {
  return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}

(3) newScheduledThreadPool

  • 它用来处理延时任务或定时任务
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize,ThreadFactory threadFactory) {
  return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize){
  return new ScheduledThreadPoolExecutor(corePoolSize);
}

(4) newSingleThreadExecutor

  • 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
  • 创建一个定长线程池,支持定时及周期性任务执行
  • 采用的阻塞队列为LinkedBlockingQueue
public static ExecutorService newSingleThreadExecutor() {
  return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
}

3. 注意事项

  • 线程资源必须通过线程池体提供,不允许在应用中自行显示创建线程

  • 线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理会规避资源耗尽的风险

  • Executors 返回的线程池对象的弊端:

    (1) FixedThreadPool 和 SingleThreadpool:

    允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM

    (2) CachedThreadPool:

    允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM