0%

java中的线程池

java中的线程池

java的线程池是运用场景最多的并发框架。合理运用线程池有3个好处

  • 降低资源消耗。降低线程创建和销毁的消耗
  • 提高响应速度。当任务到达时,不需要等待线程创建完就能立即执行
  • 提高线程的可管理性。使用线程池可以进行统一分配,调控和监控。

线程池的实现原理

处理流程如下:

  • 线程池判断核心线程池里的线程是否都在执行任务。如果不是,创建一个新的工作线程来执行任务。如果是,则进入下一个流程。
  • 线程池判断工作队列是否已满。如果没有满,则将新提交的任务存储在这个工作队列中,如果工作队列满了,则进入下一个流程。
  • 线程池判断线程池的线程是否都处于工作状态,如果没有,则创建一个新的线程执行任务,如果慢了,则交给饱和策略来处理任务。

ThreadPoolExecutor执行execute方法分为下面4个情况

  • 如果当前运行的线程小于corePoolSize,则创建新线程执行任务(这一步需要获取全局锁)
  • 如果运行的此案成等于或大于corePoolSize,则将任务加入BlockingQueue
  • 如果无法将任务加入BlockingQueue,则创建新的线程处理
  • 如果创建新的线程会使得当前运行的线程大于maximumPoolSize,则任务被拒绝,执行rejectedExecution()方法

线程的使用

线程池的创建

new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,millisseconds,runnableTaskQueue,handler)

参数的作用

  • corePoolSize:核心大小数
  • runnableTaskQueue:任务队列,用于保存等待执行的阻塞队列,可以选择下面几个
    • ArrayBlockingQueue:基于数组是我有界阻塞队列,按照FIFO排序
    • LinkedBlockingQueue:基于链表的阻塞队列,吞吐量高于ArrayBlockingQueue,静态工厂方法Executors.newFixedThreadPool使用了这个队列
    • SynchronousQueue:不存储元素的阻塞队列,Executors.newCachedThreadPool使用了这个队列
    • PriorityBlockingQueue:具有优先级的无限阻塞队列
    • DelayQueue:DelayQueue封装了一个PriorityQueue,这个queue会对队列中的ScheduledFutureTask进行排序。time小的放到前面
  • maximumPoolSize:线程池最大数量。如果队列满了,并且已创建的线程数小于最大线程数,则线程池创建新的线程执行任务。
  • ThreadFactory:用于设置创建线程的工厂,通过线程工厂给每个创建出来的线程可以设置名称。
  • RejectedExecutionHandler:饱和策略。当队列和线程都满了,说明线程池处于饱和状态,必须采取一种策略处理提交的新任务,默认是AbortPolicy。下面是java的4中策略
    • AbortPolicy:直接抛出异常
    • CallerRunsPolicy:只用调用者所在的线程来执行任务
    • 丢弃队列里最近的一个任务,来执行当前任务
    • DiscardPolicy:不处理,丢弃掉。
    • 或者自定义策略,记录日志等。
  • keepAliveTime:线程活动保持时间。线程池的工作线程空闲后,保持存活的时间。所以如果任务很多,每个任务执行的时间比较短,可以跳大事件,提高线程的利用率。
  • TimeUnit:线程活动保持时间的单位。

向线程池提交任务

两个方法,execute和submit

execute用于提交不需要返回的任务。

submit用于提交需要返回的任务。线程池会返回future对象,通过future.get()获取返回值。get()方法会阻塞到任务完成。

如何合理的配置线程池

  • CPU密集型的应配置尽可能小的线程。如N(CPU) + 1个线程的线程池。
  • IO密集的任务县城不一定一直在执行任务,则配置尽可能多。如2 * N(CPU)

线程的监控

线程监控可以使用以下的属性

  • taskCount:线程池需要执行的任务数量
  • completedTaskCount:线程池在运行过程中已完成的任务数量,小于或等于taskCount
  • largestPoolSize:线程池里曾经创建过的最大线程数量,通过这数据可以知道线程池是否曾经满过。
  • getActiveCount:获取活动的线程数。
  • 通过继承线程池来自定义线程池,重写线程池的beforeExecute,afterExecute,terminated方法。
~~