java线程池的使用

背景

刷新缓存时会有线程被阻塞。为避免阻塞用户线程,把刷新线程放到后台执行。
当key过期,有新用户读取cache时,开启一个新线程去load数据库数据,用户线程返回老的值。
因此需要添加一个线程池来刷新cache。

线程池的学习与使用

ThreadPoolExecutor类

ThreadPoolExecutor类继承AbstractExecutorService类,并提供了四个构造器。

  • public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue);
  • public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory);
  • public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler);
  • public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler);
    其中前三个构造器都是调用第四个构造器进行的初始化工作。

构造器各参数含义

  • corePoolSize:核心池大小。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。
  • maximumPoolSize:线程池最大线程数,表示在线程池中最多能创建多少个线程。
  • keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0。
  • unit:参数keepAliveTime的时间单位。
  • workQueue:一个阻塞队列,用来存储等待执行的任务。
  • threadFactory:线程工厂,主要用来创建线程。
  • handler:表示当拒绝处理任务时的策略,有四种取值:
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

ThreadPoolExecutor类中重要方法

  • execute(): 通过这个方法可以向线程池提交一个任务,交由线程池去执行。
  • submit(): 这个方法也是用来向线程池提交任务的,但是它能够利用了Future来获取任务执行结果。
  • shutdown(): 关闭线程池,使线程池处于SHUTDOWN状态,此时线程池不能接受新的任务,它会等待所有任务执行完毕。
  • shutdownNow(): 关闭线程池,使线程池处于STOP状态,此时线程池不能接受新的任务,并且会去尝试终止正在执行的任务。

任务提交给线程池之后到被执行的过程

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

线程初始化

默认情况下,创建线程池之后,线程池中是没有线程的,需要提交任务之后才会创建线程。
如果需要线程池创建之后立即创建线程,可以采用下列两种方法预创建线程。

  • prestartCoreThread():初始化一个核心线程。
  • prestartAllCoreThreads():初始化所有核心线程。
    最后执行线程会阻塞在getTask方法中的r = workQueue.take(); 即等待任务队列中有任务。

任务缓存队列

任务缓存队列用来存放等待执行的任务,类型为BlockingQueue,通常可以取下面三种类型

  • ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小。
  • LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE。
  • synchronousQueue:这个队列不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。

线程池容量的动态调整

  • setCorePoolSize:设置核心池大小。
  • setMaximumPoolSize:设置线程池最大能创建的线程数目大小。

参考

posted @ 2022-03-11 14:45  zjcfrancis  阅读(205)  评论(0编辑  收藏  举报