Java线程池总结

Java线程池总结

  • 线程池概念
  • 创建线程池
  • 执行过程
  • 拒绝策略
  • 异常处理
    • 1、try/catch
    • 2、通过submit返回对象 Future获取
    • 3、为线程设置UncaughtExceptionHandler,在uncaughtException方法中处理异常
  • 常用的线程池及对应使用的队列
    • 一、newFixedThreadPool (固定数目线程的线程池)
      • 流程
      • 特点
      • LinkedBlockingQueue(可设置容量队列)
      • 使用场景
    • 二、newCachedThreadPool(可缓存线程的线程池)
      • 特点
      • 流程
      • SynchronousQueue(同步队列)
      • 使用场景
    • 三、newScheduledThreadPool(定时及周期执行的线程池)
      • 特点
      • 流程
      • DelayQueue(延迟队列)
      • 使用场景
    • 四、newSingleThreadExecutor(单线程的线程池)
      • 特点
      • 流程
    • 其他队列
      • ArrayBlockingQueue(有界队列)
      • PriorityBlockingQueue(优先级队列)

线程池概念

线程池: 简单理解,它就是一个管理线程的池子。
  1. 它帮我们管理线程,避免增加创建线程和销毁线程的资源损耗。因为线程其实也是一个对象,创建一个对象,需要经过类加载过程,销毁一个对象,需要走GC垃圾回收流程,都是需要资源开销的。
  2. 提高响应速度。 如果任务到达了,相对于从线程池拿线程,重新去创建一条线程执行,速度肯定慢很多。
  3. 重复利用。 线程用完,再放回池子,可以达到重复利用的效果,节省资源。

创建线程池

public ThreadPoolExecutor(int corePoolSize, //核心线程数,池中所保存的线程数,包括空闲线程。
        int maximumPoolSize,				//池中允许的最大线程数。
        long keepAliveTime,					//当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。即非核心线程空闲的最大存活时间
        TimeUnit unit,						//存活时间单位。
        BlockingQueue workQueue,		//执行前用于保持任务的队列。此队列仅保持由 execute 方法提交的 Runnable 任务。
        ThreadFactory threadFactory,			//执行程序创建新线程时使用的工厂。
        RejectedExecutionHandler handler)	//由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。(拒绝策略)

执行过程

  • 提交一个任务,线程池里存活的核心线程数小于线程数corePoolSize时,线程池会创建一个核心线程去处理提交的任务。

  • 如果线程池核心线程数已满,即线程数已经等于corePoolSize,一个新提交的任务,会被放进任务队列workQueue排队等待执行。

  • 当线程池里面存活的线程数已经等于corePoolSize了,并且任务队列workQueue也满,判断线程数是否达到maximumPoolSize,即最大线程数是否已满,如果没到达,创建一个非核心线程执行提交的任务。

  • 如果当前的线程数达到了maximumPoolSize,还有新的任务过来的话,直接采用拒绝策略处理。

拒绝策略

(1)AbortPolicy: 该策略抛出RejectedExecutionException,调用者捕获异常后进行处理。

(2)DiscartPolicy:当新任务无法加入到队列中时,该策略抛弃该任务。(直接丢弃任务)

(3)DiscardOldestPolicy:放弃最旧的未处理请求,然后重试 execute;如果执行程序已关闭,则会丢弃该任务。

(4)CallerRunsPolicy:直接在 execute 方法的调用线程中运行被拒绝的任务,即将任务回退到调用者。

异常处理

1、try/catch

一段会有异常的代码
Java线程池总结_第1张图片
可以看到,没有结果输出,但也没有异常抛出,这样无法感知到任务中出现的异常,所以要try/catch。
Java线程池总结_第2张图片这样就捕获到异常。

2、通过submit返回对象 Future获取

Java线程池总结_第3张图片
Future表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,如有必要,计算完成前可以阻塞此方法。取消则由 cancel 方法来执行。还提供了其他方法,以确定任务是正常完成还是被取消了。一旦计算完成,就不能再取消计算。如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明 Future 形式类型、并返回 null 作为底层任务的结果。

Java线程池总结_第4张图片

3、为线程设置UncaughtExceptionHandler,在uncaughtException方法中处理异常

Java线程池总结_第5张图片

常用的线程池及对应使用的队列

一、newFixedThreadPool (固定数目线程的线程池)

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

流程

  1. 提交任务

  2. 如果线程数少于核心线程,创建核心线程执行任务

  3. 如果线程数等于核心线程,把任务添加到LinkedBlockingQueue阻塞队列

  4. 如果线程执行完任务,去阻塞队列取任务,继续执行。

特点

  • 核心线程数和最大线程数大小一样

  • 没有所谓的非空闲时间,即keepAliveTime为0

  • 阻塞队列为无界队列LinkedBlockingQueue

LinkedBlockingQueue(可设置容量队列)

LinkedBlockingQueue==(可设置容量队列)基于链表结构的阻塞队列,按FIFO排序任务,容量可以选择进行设置,不设置的话,将是一个无边界的阻塞队列,最大长度为Integer.MAX_VALUE,吞吐量通常要高于ArrayBlockingQuene;newFixedThreadPool线程池使用了这个队列

 //创建一个容量为 Integer.MAX_VALUE 的 LinkedBlockingQueue。
public LinkedBlockingQueue()
//创建一个具有给定(固定)容量的 LinkedBlockingQueue。 capacity - 队列的容量 
public LinkedBlockingQueue(int capacity)
//创建一个容量是 Integer.MAX_VALUE 的 LinkedBlockingQueue,
//最初包含给定 collection 的元素,元素按该 collection 迭代器的遍历顺序添加。
public LinkedBlockingQueue(Collection<? extends E> c)

使用场景

FixedThreadPool 适用于处理CPU密集型的任务,确保CPU在长期被工作线程使用的情况下,尽可能的少的分配线程,即适用执行长期的任务。


二、newCachedThreadPool(可缓存线程的线程池)

//需要时使用提供的 ThreadFactory 创建新线程。
 public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }
/*	对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。
	调用 execute 将重用以前构造的线程(如果线程可用)。
	如果现有线程没有可用的,则创建一个新线程并添加到池中。
	终止并从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线池不会使用任何资源。
	注意,可以使用 ThreadPoolExecutor 构造方法创建具有类似属性但细节不同(例如超时参数)的线程池
*/
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

特点

  • 核心线程数为0

  • 最大线程数为Integer.MAX_VALUE

  • 阻塞队列是SynchronousQueue

  • 非核心线程空闲存活时间为60秒


流程

  1. 提交任务

  2. 因为没有核心线程,所以任务直接加到SynchronousQueue队列。

  3. 判断是否有空闲线程,如果有,就去取出任务执行。

  4. 如果没有空闲线程,就新建一个线程执行。

  5. 执行完任务的线程,还可以存活60秒,如果在这期间,接到任务,可以继续活下去;否则,被销毁。

SynchronousQueue(同步队列)

SynchronousQueue==(同步队列)一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene,newCachedThreadPool线程池使用了这个队列。

使用场景

用于并发执行大量短期的小任务。

三、newScheduledThreadPool(定时及周期执行的线程池)

//安排在给定延迟后运行命令或者定期地执行。
 public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
 //
 public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
    }

特点

  • 最大线程数为Integer.MAX_VALUE

  • 阻塞队列是DelayedWorkQueue

  • keepAliveTime为0

  • scheduleAtFixedRate() :按某种速率周期执行

  • scheduleWithFixedDelay():在某个延迟后执行

流程

  1. 添加一个任务

  2. 线程池中的线程从 DelayQueue 中取任务

  3. 线程从 DelayQueue 中获取 time 大于等于当前时间的task

  4. 执行完后修改这个 task 的 time 为下次被执行的时间

  5. 这个 task 放回DelayQueue队列中

DelayQueue(延迟队列)

DelayQueue(延迟队列)是一个任务定时周期的延迟执行的队列。根据指定的执行时间从小到大排序,否则根据插入到队列的先后排序。newScheduledThreadPool线程池使用了这个队列。

使用场景

周期性执行任务的场景,需要限制线程数量的场景

四、newSingleThreadExecutor(单线程的线程池)

/*
  创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
  (注意,如果因为在关闭前的执行期间出现失败而终止了此单个线程,那么需要,
  一个新线程将代替它执行后续的任务)。可保证顺序地执行各个任务,
  并且在任意给定的时间不会有多个线程是活动的。
  与其他等效的 newFixedThreadPool(1) 不同,
  可保证无需重新配置此方法所返回的执行程序即可使用其他的线程。
*/
 public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
 public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }

特点

  • 核心线程数为1

  • 最大线程数也为1

  • 阻塞队列是LinkedBlockingQueue

  • keepAliveTime为0

流程

  1. 提交任务

  2. 线程池是否有一条线程在,如果没有,新建线程执行任务

  3. 如果有,将任务加到阻塞队列

  4. 当前的唯一线程,从队列取任务,执行完一个,再继续取,一个人(一条线程)夜以继日地干活。

其他队列

ArrayBlockingQueue(有界队列)

ArrayBlockingQueue(有界队列)是一个用数组实现的有界阻塞队列,按FIFO排序量。

PriorityBlockingQueue(优先级队列)

PriorityBlockingQueue(优先级队列)是具有优先级的无界阻塞队列;

你可能感兴趣的:(java,并发编程)