关于线程池的思考

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

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

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

  • CacheThreadPool
  • FixedThreadPool
  • ScheduedThreadPool
  • SingleThreadExcutor

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 ,它提供了很多参数可以进行细粒度的控制。

  • 将任务队列设置成有边界的队列
  • 使用合适的 RejectionHandler - 自定义的 RejectionHandler 或 JDK 提供的默认 handler 。
  • 如果在任务完成前后需要执行某些操作,可以重载
    beforeExecute(Thread, Runnable)
    afterExecute(Runnable, Throwable)
    
  • 重载 ThreadFactory ,如果有线程定制化的需求
  • 在运行时动态控制线程池的大小(Dynamic Thread Pool

学习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)

参数说明

  • corePoolSize
    核心线程数:默认情况下核心线程会一直存活,即使处于闲置状态也不会受存keepAliveTime限制。除非将allowCoreThreadTimeOut设置为true。
  • maximumPoolSize
    线程池所能容纳的最大线程数。超过这个数的线程将被阻塞。当任务队列为没有设置大小的LinkedBlockingDeque时,这个值无效。
  • keepAliveTime
    非核心线程的闲置超时时间,超过这个时间就会被回收。
  • unit
    指定keepAliveTime的单位,如TimeUnit.SECONDS。当将allowCoreThreadTimeOut设置为true时对corePoolSize生效。
  • workQueue
    线程池中的任务队列.常用的有三种队列,
    SynchronousQueue:同步队列
    LinkedBlockingDeque:
    ArrayBlockingQueue:
  • threadFactory
    线程工厂,提供创建新线程的功能。ThreadFactory是一个接口,只有一个方法
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;
  }
}
  • RejectedExecutionHandler
    RejectedExecutionHandler也是一个接口,只有一个方法
public interface RejectedExecutionHandler {
  void rejectedExecution(Runnable var1, ThreadPoolExecutor var2);
}

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

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

  • 方法一:使用while(1)方式,不停的轮询任务状态,可以实现功能,但肯定不是我们要的结果,因为这样的主线程并不是挂起,只是不断循环等待的,一样需要耗费资源,如果线程过多会非常浪费资源。
  • 方式二:使用future.get()方法,
  • 方式三:在执行任务的程序的run()方法的最后用wait/notify,唤醒主线程,真正实现了异步,主线程并不多耗费资源。缺点是不够灵活,例如:更改了执行的任务,就必须在新任务的run()方法后加入唤醒操作,不能做到与任务无关。
    -方式四:用wait/notify和挂钩程序及反射机制的应用,实现了线程池间的通信控制。

参考资料:
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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354

推荐阅读更多精彩内容