Java基础-线程池

  Java应用程序需要运用线程,线程的出现使得程序能进行并行化操作。但如果遇上并行化量大,且运行时间短的场景来说,每个任务都创建一个线程,其实并不合适,启动和销毁线程占用大量的资源,所以,用线程池,重复使用线程完成任务。

一、线程池的基本概念

  已知一个工厂的流水线上有正式工人10人,工厂刚开工任务单比较少,每来一个工作单就让一个空闲的工人离开办公室安排到生产线上劳作,当任务单比较多,10个工人都在劳作,就把新进入的工作单放到流水线上,只要有工人空闲就继续工作,不用回办公室了。突然有一天接了一个大单,工作量猛增,完全超出了流水线的负荷,这个时候聘请10个临时工帮忙生产,如果再增加工单,评估下来超过这20人的承受能力,经理就考虑饱和策略。如果工作量一点点减轻,就要慢慢解聘临时工。直到没有工单的时候,经理要考虑是不是把做正式工遣返回办公室喝茶。
  以上就是线程池的概念。以下列出了线程池的一些概念:

1、corePoolSize

  核心线程池大小,这就就是上述的10名正式员工。线程池创建完一般没有线程,只有当任务陆续到来的时候才会一个一个被创建直到这个corePoolSize大小。当然也可以在任务到来前先创建起来(prestartAllCoreThreads()或者 prestartCoreThread())。

2、maximumPoolSize

  线程池最大数量,这个值就是10+4,最大数量不能超出这个数。

3、workQueue

  工作队列,在执行任务之前用于保存任务的队列,这个队列涉及到后面的饱和策略。这个队列父类是一个阻塞队列,一般使用的LinkedBlockingQueue和SynchronousQueue会比较多。

4、keepAliveTime

  这个只有当当前线程数大于corePoolSize的时候才会起作用,这个目的是让那些空闲的临时工在没有工单的一段时间内解聘,直到当前线程数达到核心线程数。如果设置了allowCoreThreadTimeOut(boolean),那么核心线程数也会被清理,直到当前线程数为0。

5、TimeUnit

  keepAliveTime空闲时间的参数的单位,可以是天、小时、分钟、秒…

6、ThreadFactory

  创建线程的工厂。

7、RejectedExecutionHandler

  表示当拒绝处理任务时的策略。

  • ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
  • ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
  • ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
  • ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

总结

  • 如果当前线程池中的线程数目小于corePoolSize,则每来一个任务,就会创建一个线程去执行这个任务;
  • 如果当前线程池中的线程数目>=corePoolSize,则每来一个任务,会尝试将其添加到任务缓存队列当中,若添加成功,则该任务会等待空闲线程将其取出去执行;若添加失败(一般来说是任务缓存队列已满),则会尝试创建新的线程去执行这个任务;
  • 如果当前线程池中的线程数目达到maximumPoolSize,则会采取任务拒绝策略进行处理;
  • 如果线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数目不大于corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。

二、四种线程池模型

  ThreadPoolExecutor的线程池构造需要了解一些内在参数含义才能配置的很好,对于一些通用的场景的线程池,有一些通用配置,就是下面的4种线程池模型。

  • Executors.newCachedThreadPool 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
  • Executors.newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  • Executors.newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
  • Executors.newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

1、Executors.newCachedThreadPool

 new ThreadPoolExecutor(0, Integer.MAX_VALUE,
               60L, TimeUnit.SECONDS,
               new SynchronousQueue<Runnable>());

  根据代码可以知道核心线程数为0,所以所有的线程都是临时工,要被回收的。这个线程池的重点就是SynchronousQueue,这个队列SynchronousQueue是无界的,是一种无缓冲的等待队列,但是由于该Queue本身的特性,在某次添加元素后必须等待其他线程取走后才能继续添加。当没有工作线程可用的时候,offer()返回一个false,这个时候再去创建线程去take等待,再次尝试offer就可以交给线程去处理了。

2、Executors.newFixedThreadPool

new ThreadPoolExecutor(nThreads, nThreads,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>());

  核心线程的数量=最大线程数量,这样设置就取消临时工的数量,所以,理论上超时时间就不需要,多余处理不下的任务全放到队列中,队列用来链表,所以理论上不用担心放不下。

3、Executors.newScheduledThreadPool

//类被封装,最底层的初始化
new ThreadPoolExecutor(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());

  这个类和他内在的队列都特殊处理过,这个队列可以定时执行某个任务,可以延迟执行某任务,功能上比较强大。详细了解可以看一下源码。

4、Executors.newSingleThreadExecutor

new ThreadPoolExecutor(1, 1,
               0L, TimeUnit.MILLISECONDS,
               new LinkedBlockingQueue<Runnable>())

  核心队列就一个,一个线程慢慢处理。这个类也是被封装了,详细了解可以看一下源码。

发布了63 篇原创文章 · 获赞 8 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/myt0929/article/details/84329105