Loading

线程池

线程池

线程池就是一个管理一组线程的池子,当有任务要执行的时候就会从中获取线程来执行,执行完之后线程并不会销毁,而是等待下一个任务。使用线程池可以避免创建线程产生的资源的开销,提高响应速度,更好的管理线程,避免大量线程的创建。



常见的线程池

  • newFixedThreadPool (固定数目线程的线程池)
  • newCachedThreadPool (可缓存线程的线程池)
  • newSingleThreadExecutor (单线程的线程池)
  • newScheduledThreadPool (定时及周期执行的线程池)


重要参数

  1. corePoolSize

此值是用来初始化线程池中核心线程数,当线程池中线程池数< corePoolSize时,系统默认是添加一个任务才创建一个线程池。当线程数 = corePoolSize时,新任务会追加到workQueue中。

如何确定核心线程数:

计算机的任务大致分为:

  • 计算密集型:大部分都在用CPU跟内存,加密,逻辑操作业务处理等。需要大量使用CPU,应该减少上下文切换 corePoolSize+1

  • IO密集型:数据库链接,网络通讯传输等。 corePoolSize*2

  1. maximumPoolSize

maximumPoolSize表示允许的最大线程数 = (非核心线程数+核心线程数),当BlockingQueue也满了,但线程池中总线程数 < maximumPoolSize时候就会再次创建新的线程。

  1. keepAliveTime

非核心线程 =(maximumPoolSize - corePoolSize ) ,非核心线程闲置下来不干活最多存活时间。

  1. workQueue

线程池等待队列,维护着等待执行的Runnable对象。当运行当线程数= corePoolSize时,新的任务会被添加到workQueue中,如果workQueue也满了则尝试用非核心线程执行任务,等待队列应该尽量用有界的。

  • ArrayBlockingQueue:ArrayBlockingQueue(有界队列)是一个用数组实现的有界阻塞队列,按FIFO排序量。
  • LinkedBlockingQueue:LinkedBlockingQueue(可设置容量队列)是基于链表结构的阻塞队列,按FIFO排序任务,容量可以选择进行设置,不设置的话,将是一个无边界的阻塞队列,最大长度为Integer.MAX_VALUE,吞吐量通常要高于ArrayBlockingQuene;
  1. threadFactory

创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等。

  1. handler

corePoolSizeworkQueuemaximumPoolSize都不可用的时候执行的饱和策略。

  • AbortPolicy :直接抛出异常,默认使用此策略
  • CallerRunsPolicy:用调用者所在的线程来执行任务
  • DiscardOldestPolicy:丢弃阻塞队列里最老的任务,也就是队列里靠前的任务
  • DiscardPolicy :当前任务直接丢弃

想实现自己的拒绝策略,实现RejectedExecutionHandler接口即可



执行原理

  1. 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。
  2. 当调用 execute() 方法添加一个任务时,线程池会做如下判断:
  • 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
  • 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列;
  • 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;
  • 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会根据拒绝策略来对应处理。
  1. 当一个线程完成任务时,它会从队列中取下一个任务来执行。 非核心线程,核心线程都会。

  2. 当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。

posted @ 2023-09-02 15:01  花园SON  阅读(4)  评论(0编辑  收藏  举报