搜公众号
推荐 原创 视频 Java开发 开发工具 Python开发 Kotlin开发 Ruby开发 .NET开发 服务器运维 开放平台 架构师 大数据 云计算 人工智能 开发语言 其它开发 iOS开发 前端开发 JavaScript开发 Android开发 PHP开发 数据库
Lambda在线 > 参赛博文| 线程池

参赛博文| 线程池

2019-01-19
举报


比特科技
参赛博文| 线程池   参赛博文| 线程池


什么是线程池?


所谓线程池就是一个存放线程的地方,由一些线程组成,其内部有许多已经创建好的线程等待调度和使用。


当然,他还含有其他的一些东西,比如工作队列。


参赛博文| 线程池


线程池的优点


(1)降低资源消耗,通过重复利用已经创建好的线程来降低线程创建与销毁所带来的资源消耗。

(2)提高响应速度,当任务到达时,无需等待线程创建可以立即执行。

(3)提高线程的可管理性,使用线程池可以统一进行线程分配、调度和监控。


线程池的执行流程


第一步:判断核心线程池是否已满,如果未满,创建一个新的线程来执行任务; 如果已满,判断是否有空闲线程,如果有,将任务分配给空闲线程,如果没有执行步骤二。.


第二步:判断工作队列是否已满,如果工作队列未满,将任务存储在工作队列中等待空闲线程调度,如果工作队列已满,执行步骤三。


第三步:判断当前线程池线程数量是否已达最大值,如果未达最大值,则创建新的线程来指向任务,如果已达最大值,则执行步骤四。


第四步:调用饱和策略来处理此任务。


参赛博文| 线程池


线程池的使用


手工创建线程池:


通过 new 一个 ThreadPoolExecutor  就可以实现自定义线程池。


public ThreadPoolExecutorint corePoolSize,
     int maximumPoolSize,
     long keepAliveTime,
     TimeUnit unit,
     BlockingQueue<Runnable>workQueue,
     ThreadFactory threadFactory,
     RejectedExecutionHandlerhandler )


参数解析:


(1)corePoolSize(核心线程池大小):


当提交一个任务到线程池时,只要核心线程池未达到corePoolSize ,就创建新线程来执行任务。( 调用 preStarAllCoreThreads() 方法,线程池会提前创建并启动所有核心线程  )      


(2)maximumPoolSize:线程池最大数量


关于核心线程池数与线程池最大数:

线程池中的线程分为核心线程与非核心线程,


参赛博文| 线程池


即使在没有任务的时候,核心线程被创建后就不会被回收,只会处于空闲状态。

非核心线程在空闲后的  keepAliveTime 时间后,就会被回收,不再存活。



 线程池的工作线程空闲后,保持存活的时间。

 任务比较多,并且每个任务执行的时间比较短,可以调大此参数来提高线程利用率。


(4)unit:参数的时间单位


TimeUnit是一个枚举类型,其包括:

NANOSECONDS : 微毫秒

MICROSECONDS : 微秒

MILLISECONDS : 毫秒 

SECONDS : 秒

MINUTES : 分

HOURS : 小时

DAYS : 天


(5)BlockingQueue<Runnable>workQueue(任务队列):

 保存等待执行任务的阻塞队列。       


 有四个内置的阻塞队列,可以供我们选择;


  ---ArrayBlockingQueue:基于数组有界阻塞队列,安照FIFO(先进先出)原则对元素进行排序。            


  ---LinkedBlockingQueue:基于链表的无界阻塞队列,按照FIFO排列元素,

       吞吐量要高于ArrayBlockingQueue。

      内置线程池newFixedThredThreadpool(固有大小线程池)就采用此队列;


  ---SynchronousQueue:一个不存储元素的阻塞队列(无界队列)。每个插入操作需要等待另外一个线程的移除操作,否则插入操作一   直处于阻塞状态。

       吞吐量通常高于 LinkedBlockingqueue。

      内置线程池 newCachedThreadPool(缓存线程池)就采用此队列。


  ---PriorityBlockingQueue:具有优先级的阻塞队列。


什么是阻塞队列呢?


当向队列中提交的任务超过队列容量的最大值时,

阻塞式队列会进行等待,而等待时间根据队列设置的超时时间,如果在这个时间之内,队列中有任务出队了,就可以将任务添加进去。

而非阻塞式没有这种等待措施,会出现信息丢失。


(6)threadFactory 


执行程序创建新的线程时使用的工厂


(7)RejectedExcecutionHandler:饱和策略;(默认采用的策略是:AborPolicy)


  共有四种策略:


  ---AbortPolicy:无法处理新任务抛出异常(JDK 默认采用此策略)


  ---CallerRunsPolicy:使用调用者所在线程来处理任务;


  ---DisCardOldestPolicy:丢弃队列中最近的一个任务并执行当前任务;


   ---DisCardPolicy:不处理任务,丢弃任务,也不报异常。


关闭线程池:


shutdown();


class MyThread implements Runnable{

@Override
public void run() {
for(int i=0;i<3;i++){
//打印当前线程的名字和数字i
System.out.println(Thread.currentThread().getName()+"."+i);
       }
   }
}

public class Java_1203 {
public static void main(String[] args) {
MyThread myThread = new MyThread();

//通过 new ThreadPoolExecutor 类自定义一个线程池;
ExecutorService executorService 
new ThreadPoolExecutor(352000,TimeUnit.MILLISECONDS,new LinkedBlockingDeque<>())
;

//核心线程池大小:3
//线程池最大数量:5
//线程存活时间:2000毫秒
//任务队列:LinkedBlockingDeque
//默认采用的 AborPolicy 饱和策略

    for(int i=0;i<5;i++){
    //把任务提交给线程池
    executorService.execute(myThread);
       }
   //关闭线程池
   executorService.shutdown();
   }
}


运行结果


pool-1-thread-2.0
pool-1-thread-3.0
pool-1-thread-1.0
pool-1-thread-3.1
pool-1-thread-2.1
pool-1-thread-3.2
pool-1-thread-1.1
pool-1-thread-2.2
pool-1-thread-1.2
pool-1-thread-3.0
pool-1-thread-2.0
pool-1-thread-3.1
pool-1-thread-2.1
pool-1-thread-3.2
pool-1-thread-2.2

Process finished with exit code 0


由运行结果我们可以看到,一共创建了三个线程来执行任务,而且这三个线程都是核心线程。

这是因为当任务到来时,只要核心线程没有达最大值,都会创建新的线程。

当核心线程达最大值时,它们仨其实已经能够完美处理所有的任务,也就不会再创建新的线程来执行任务。


内置四大线程池:


(1)固定大小线程池 FixedThreadPool  


固定大小线程池试用于为了满足资源管理需求,而需要限制当前线程数量的应用。

适用于负载较重的服务器。



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


FixedThreadPool 使 用 无 界 队 列 LinkedBlockingQueue 作 为 线 程 池 的 工 作 队 列 ( 队 列 的 容 量 为 Integer.MAX_VALUE(也就是整形最大值,所以并不是真的无界,而是伪无界))。


使用无界队列作为工作队列会对线程池带来如下影响。


1)当线程池中的线程数达到corePoolSize后,新任务将在无界队列中等待,因此线程池中的线程数不会超过 corePoolSize。 也就不会有非核心线程被创造。


2)由于1,使用无界队列时maximumPoolSize将是一个无效参数。


3)由于1和2,使用无界队列 时keepAliveTime将是一个无效参数。


4)由于使用无界队列,运行中的FixedThreadPool(未执行方法 shutdown()或 shutdownNow())不会拒绝任务(不会调用RejectedExecutionHandler.rejectedExecution方法)。


这个线程池长这样:


参赛博文| 线程池

 

(2)单线程池 SingleThreadExecutor

适用于需要保证顺序执行各个任务,并且在任意时间,不会有多个任务。


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


SingleThreadExecutor 的 corePoolSize 和 maximumPoolSize 被 设 置 为 1 。

其 他 参 数 与 FixedThreadPool 相 同 。


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

( 队 列 的 容 量 为 Integer.MAX_VALUE)。


这个线程池长这样:


参赛博文| 线程池


所有的任务都这一个线程自己一个人干,暂时干不了就存着,保证了任务的执行的顺序。


(3)缓存线程池 CachedThreadPool


 根据需要创建新线程。

 当提交任务速度快于执行任务速度,缓存线程池会不断创建新线程。

 应用场景:大小无界线程池,适用于执行很多的短期异步小程序,或者负载较轻的服务器



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


CachedThreadPool的corePoolSize被设置为0,即corePool为空;


maximumPoolSize被设置为Integer.MAX_VALUE,即 maximumPool是无界的。


这里把keepAliveTime设置为60L,意味着CachedThreadPool中的空闲线程等待新任务的 长时间为60秒,空闲线程超过60秒后将会被终止。


FixedThreadPool 和 SingleThreadExecutor 使 用 无 界 队 列 LinkedBlockingQueue 作 为 线 程 池 的 工 作 队 列 。 CachedThreadPool使用没有容量的SynchronousQueue作为线程池的工作队列,但CachedThreadPool的maximumPool是 无界的。这意味着,如果主线程提交任务的速度高于maximumPool中线程处理任务的速度时,CachedThreadPool会 不断创建新线程。极端情况下,CachedThreadPool会因为创建过多线程而耗尽CPU和内存资源。


其实这一大段看似复杂话所表达的情况就是:

如果一个游泳池的 排水速度 慢于 进水速度,那么水量会一直上涨,直到水位爆表。。。。。


这个线程池长这样:


参赛博文| 线程池


(4)定时调度池 ScheduledThreadPool(int nThread)


应用于需要定时执行任务的应用场景。


public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
       return new ScheduledThreadPoolExecutor(corePoolSize);
   }


ScheduledThreadPoolExecutor继承自ThreadPoolExecutor。

它主要用来在给定的延迟之后运行任务,或者定期执行任务。

 

通过查看四大线程池的源代码我们可以看出,

其实这四大内置的线程池与我们自定义线程池具有相同的方法,都是通过直接或间接 new  ThreadPoolExecutor ()。

只不过的是内置的线程池已经帮我们把参数设置好了而已。


这就好比我们自己在家做饭和在饭店店买饭一样。

在家做饭就是自定义线程池。

饭店买的饭就是内置的线程池。



如果你喜欢这篇文章

请继续关注博文大赛

为你喜欢的作者打call哦!

End


编辑:文艺

审核:啊琛琛

导师微信 15596668826

导师QQ 2799935869


导师Tel / 15249287076

版权声明:本站内容全部来自于腾讯微信公众号,属第三方自助推荐收录。《参赛博文| 线程池》的版权归原作者「西安比特教育」所有,文章言论观点不代表Lambda在线的观点, Lambda在线不承担任何法律责任。如需删除可联系QQ:516101458

文章来源: 阅读原文

相关阅读

举报