java线程池——面试常问

2021/4/19 20:25:29

本文主要是介绍java线程池——面试常问,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

线程池面试常问

参考blog
https://zhuanlan.zhihu.com/p/73990200

java线程池 最大线程和核心线程

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

最大线程数:就是线程池里面能够容纳的最大线程容量
核心线程:我的理解就是 线程池中常驻线程,不会被销毁的线程。主要区别于 非核心线程

Java的线程池说一下,各个参数的作用,如何进行的?

// java中的线程池是由ThreadPoolExecutor实现的

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {

}
  1. 参数含义

    • corePoolSize 核心线程数
    • maximumPoolSize 最大线程数
    • long keepAliveTime 线程池中非核心线程空闲的存活时间
    • TimeUnit unit 空闲时间单位
    • BlockingQueue workQueue 任务的阻塞队列
    • ThreadFactory threadFactory 用于创建线程的工厂
    • RejectedExecutionHandler handler 线程池饱和之后的策略。主要是四种
  2. 线程池执行流程————调用executor()方法
    avatar

    1. 首先看核心线程是否已经全部在工作。如果没有,交给核心线程执行。反之进行下一步
    2. 如果任务队列没有满,就将任务放到任务队列中阻塞。如果任务队列满了,进行下一步。
    3. 如果线程数没有超过最大线程数,那么可以创建 非核心线程 来执行任务。如果线程数 超过了 最大线程数,执行下一步
    4. 根据 RejectedExecutionHandler的类型采取拒绝策略
  3. 四种拒绝策略
    AbortPolicy(抛出一个异常,默认的)
    DiscardPolicy(直接丢弃任务)
    DiscardOldestPolicy(丢弃队列里最老的任务,将当前这个任务继续提交给线程池)
    CallerRunsPolicy(交给线程池调用所在的线程进行处理)

线程池异常处理

在使用线程池处理任务的时候,任务代码可能抛出RuntimeException,抛出异常后,线程池可能捕获它,也可能创建一个新的线程来代替异常的线程,我们可能无法感知任务出现了异常,因此我们需要考虑线程池异常情况。

四种方法:
	1. 利用try-catch捕获异常
	2. submit()执行,Future.get()接受异常
	3. 重写 ThreadPoolExecutor的afterExecute()方法,处理传递的异常引用
	4. 传入自己的ThreadFactory,设置Thread.UncaughtExceptionHandler来处理异常

线程池都有哪几种工作队列?

  1. ArrayBlockingQueue

    • 有界队列,是一个用数组实现的有界阻塞队列。 按FIFO排序
  2. LinkedBlockingQueue

    • 链表实现的阻塞队列。可以指定容量。如果不指定那么就是无界队列,最大长度为Integer.MAX_VALUE,吞吐量通常要高于ArrayBlockingQuene;newFixedThreadPool线程池使用了这个队列
  3. DelayQueue(延迟队列)

    • 任务定时周期的执行的延迟队列。根据指定执行时间的从小到大排序,否则根据插入到队列的顺序排序。newScheduledThreadPool线程池使用这个。
  4. PriorityBlockingQueue

    • 优先级队列。具有优先级的无界阻塞队列
  5. SynchronousQueue

    • 同步队列。一个不存储元素的队列。一个元素的插入必须等到另一个线程的移出操作,否则插入一直处于阻塞状态。newCachedThreadPool线程池使用了这个队列。

几种线程池

  1. newFixedThreadPool-----------固定线程池
    • 核心线程数 = 最大线程数。 空闲时间为0。 使用LinkedBlockingQueue的无界队列
    • 执行过程
      1. 提交任务。看是否有核心线程空闲,如果有直接执行。如果没有的话,加入到阻塞队列中。
    • 存在的问题:因为任务队列是无界的Integer.MAX_VALUE 可能会存在OOM(out of memory)
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
  1. newSingleThreadPool----------单一线程池
    • 核心线程数 = 最大线程数 = 1. 使用LinkedBlockingQueue 无界队列
    • 执行过程
      1. 提交任务。核心线程有空那就执行。没空就到阻塞队列中。
    • 存在问题。可能OOM
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
  1. newCachedThreadPool--------缓存线程池
    • 核心线程数 = 0, 最大线程数 = Integer.MAX_VALUE。 空闲时间60s。使用SynchronousQueue
    • 执行过程
      1. 提交任务。如果有线程空闲,就直接交给空闲线程执行。如果没有的话创建线程。
        SynchronousQueue的容量为0, 是不能够有任务阻塞在里面的
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
  1. newScheduledThreadPool--------定期线程池
    • 使用的是 DelayedWorkQueue延时队列. 最大线程数是 Integer.MAX_VALUE
    • 执行过程
      添加一个任务
      线程池中的线程从 DelayQueue 中取任务
      线程从 DelayQueue 中获取 time 大于等于当前时间的task
      执行完后修改这个 task 的 time 为下次被执行的时间
      这个 task 放回DelayQueue队列中
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

线程池状态

 	//线程池状态
   private static final int RUNNING    = -1 << COUNT_BITS;
   private static final int SHUTDOWN   =  0 << COUNT_BITS;
   private static final int STOP       =  1 << COUNT_BITS;
   private static final int TIDYING    =  2 << COUNT_BITS;
   private static final int TERMINATED =  3 << COUNT_BITS;

avatar

  1. RUNNING

    1. 该状态的线程池会接收到任务。并处理阻塞队列中的任务
    2. 如果调用 shutdown()方法就切换到 SHUTDOWN()状态
    3. 如果调用 shutdownNow()方法就切换到STOP()状态
  2. SHUTDOWN

    1. 该状态下的线程池不会接受新的任务。但是会处理阻塞队列中的任务
    2. 当阻塞队列为空,以及线程池中执行的任务也为空的时候转换为TIDYING状态
  3. STOP

    1. 该状态下的线程池不会接受新的任务。也不处理阻塞队列中的任务
    2. 线程池中执行的任务为空,进入TIDYING状态;
  4. TIDYING

    1. 该状态表示所有的任务已经终止。记录任务数为0
    2. 调用teminated()方法,执行完毕,进入TERMINATED状态
  5. TERMINATED

    1. 该状态表示线程池彻底终止


这篇关于java线程池——面试常问的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程