IT阔论

关于线程池的思考

2018-05-20  本文已影响2人  七佰

如果频繁使用线程的情况下,每次都使用New thread,有以下几点不好的地方。

  1. 性能低下。每次都需要新建线程,浪费资源。
  2. 极端情况下,一直新建线程,导致占用过多资源。
  3. 缺乏更多定时等功能。

这时,我们引入线程池的概念。可以解决上述问题。
JDK8下线程池支持四种类似。

CacheThreadPool

CacheThreadPool是通过util.concurrent.Executors创建的ThreadPoolExecutor实例,这个实例根据需要,在线程可用时,重用线程池中的线程。适用于大量的短生命周期的异步任务,可以显著提高性能。当调用execute时,重用之前已经构造的可用线程,如果不存在可用的线程,那么就会新建一个线程加入到线程池中。如果线程超过60秒未使用,那么会从缓存中移除。因此,在长时间不使用的情况下,不消耗任何资源。

FixedThreadPool

FixedThreadPool是通过util.concurrent.Executors创建的ThreadPoolExecutor实例,这个实例会复用固定数量的线程。任意时间点,至多有N个(可设置)线程可用。如果有多的任务过来,会自动在队列中等待,一直到有可用线程可以使用。所有的线程都会在线程池中,直到显式的执行 ExecutorService.shutdown()关闭.

ScheduedThreadPool

SingleThreadExcutor

SingleThreadExcutor是通过util.concurrent.Executors创建的ThreadPoolExecutor实例,这个线程只会用单个工作进程执行无边界的队列任务。他与new FixedThreadPool(1)的区别在于,当进程出现错误的时候,SingleThreadExcutor可以有新的进程继续执行,而FixedThreadPool(1)不行。

最佳实践

FixedThreadPoolCachedThreadPool两者对高负载的应用都不是特别友好。
CachedThreadPool 要比 FixedThreadPool 危险很多。
如果应用要求高负载、低延迟,最好不要选择以上两种线程池:

  • 任务队列的无边界:会导致内存溢出以及高延迟
  • 长时间运行会导致 CachedThreadPool 在线程创建上失控

因为两者都不是特别友好,所以推荐使用 ThreadPoolExecutor ,它提供了很多参数可以进行细粒度的控制。

学习ThreadPoolExecutor

先展示一下 ThreadPoolExecutor的继承关系。ExecutorService是Executor的子接口,增加了一些常用的对线程的控制方法,之后使用线程池主要也是使用这些方法。AbstractExecutorService是一个抽象类。ThreadPoolExecutor就是实现了这个类。

ThreadPoolExecutor.png

四大构造函数

ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) 
ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)

参数说明

public interface ThreadFactory {
  Thread newThread(Runnable r);
}

通过线程工厂可以对线程的一些属性进行定制。默认的工厂:

static class DefaultThreadFactory implements ThreadFactory {
  private static final AtomicInteger poolNumber = new AtomicInteger(1);
  private final ThreadGroup group;
  private final AtomicInteger threadNumber = new AtomicInteger(1);
  private final String namePrefix;
  DefaultThreadFactory() {
      SecurityManager var1 = System.getSecurityManager();
      this.group = var1 != null?var1.getThreadGroup():Thread.currentThread().getThreadGroup();
      this.namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-";
  }
  public Thread newThread(Runnable var1) {
      Thread var2 = new Thread(this.group, var1, this.namePrefix + this.threadNumber.getAndIncrement(), 0L);
      if(var2.isDaemon()) {
          var2.setDaemon(false);
      }
      if(var2.getPriority() != 5) {
          var2.setPriority(5);
      }
      return var2;
  }
}
public interface RejectedExecutionHandler {
  void rejectedExecution(Runnable var1, ThreadPoolExecutor var2);
}

当线程池中的资源已经全部使用,添加新线程被拒绝时,会调用RejectedExecutionHandler的rejectedExecution方法。

场景实践:
当我需要在任务结束的时候,继续执行操作的话,那么我应该怎么办呢?

参考资料:
https://www.cnblogs.com/zhangyasong/p/6929749.html
https://www.cnblogs.com/richaaaard/p/6599184.html
http://wawlian.iteye.com/blog/1315256
https://blog.csdn.net/qq_25806863/article/details/71126867

上一篇 下一篇

猜你喜欢

热点阅读