Java岗大厂面试百日冲刺Day47— 并发编程4(日积月累,每日三题)

Posted _陈哈哈

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java岗大厂面试百日冲刺Day47— 并发编程4(日积月累,每日三题)相关的知识,希望对你有一定的参考价值。

  11月6号的北京软考取消了,不想别的了,继续开卷!!!!!!每周2-3篇,中间穿插面试真题分享。

  大家好,我是陈哈哈,北漂五年。相信大家和我一样,都有一个大厂梦,作为一名资深Java选手,深知面试重要性,接下来我准备用100天时间,基于Java岗面试中的高频面试题,以每日3题的形式,带你过一遍热门面试题及恰如其分的解答。

  一路走来,随着问题加深,发现不会的也愈来愈多。但底气着实足了不少,相信不少朋友和我一样,日积月累才是最有效的学习方式!想起高三时一个同学的座右铭:只有沉下去,才能浮上来。共勉(juan)。


作者:爪哇小白2021 —— 博客地址



  本栏目Java开发岗高频面试题主要出自以下各技术栈:Java基础知识集合容器并发编程JVMSpring全家桶MyBatis等ORMapping框架mysql数据库Redis缓存RabbitMQ消息队列Linux操作技巧等。

  在《阿里Java开发代码规范》中表示,线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。即在使用多线程时,均要通过线程池创建

面试题1:说一下线程池都是怎么创建的?

线程池可以自动创建也可以手动创建

自动创建线程池

  自动创建体现在Executors工具类中,常见的可以创建newFixedThreadPoolnewCachedThreadPoolnewSingleThreadExecutornewScheduledThreadPool

  public static ExecutorService newFixedThreadPool(int var0) {
        return new ThreadPoolExecutor(var0, var0, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
  }
	
  public static ExecutorService newSingleThreadExecutor() {
        return new Executors.FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()));
  }
 
  public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue());
  }
 
  public static ScheduledExecutorService newScheduledThreadPool(int var0) {
        return new ScheduledThreadPoolExecutor(var0);
  }

手动创建线程池(推荐)

  手动创建体现在可以灵活设置线程池的各个参数,体现在代码中即ThreadPoolExecutor类构造器上各个实参的不同:

/**
 * @Description 配置线程池实例,用于调用
 */
@Configuration
public class ThreadPoolConfig {
    @Bean(value = "threadPoolInstance")
    public ExecutorService createThreadPoolInstance() {
	
        //通过guava类库的ThreadFactoryBuilder来实现线程工厂类并设置线程名称
        ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("thread-pool-%d").build();
        ExecutorService threadPool = new ThreadPoolExecutor(10, 16, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100), threadFactory, new ThreadPoolExecutor.AbortPolicy());
        return threadPool;
    }
}
  //通过name=threadPoolInstance引用线程池实例
  @Resource(name = "threadPoolInstance")
  private ExecutorService executorService;
 
  @Override
  public void spikeConsumer() {
    // 线程池启动线程
    executorService.execute(new Runnable() {
    @Override
    public void run() {
      // 可执行代码块儿
     }});
  }

  在《阿里Java开发代码规范》中表示,实际项目开发中推荐使用手动创建线程池的方式,而不用默认自动创建方式;

追问1:线程池都有哪些参数?

// 默认拒绝策略为 AbortPolicy
public ThreadPoolExecutor(int corePoolSize,
                           int maximumPoolSize,
                           long keepAliveTime,
                           TimeUnit unit,
                           BlockingQueue<Runnable> workQueue,
                           ThreadFactory threadFactory,
                           RejectedExecutionHandler handler) {}
  • corePoolSize核心线程数,也是线程池中常驻的线程数,线程池初始化时默认是没有线程的,当任务来临时才开始创建线程去执行任务
  • maximumPoolSize最大线程数,在核心线程数的基础上可能会额外增加一些非核心线程,需要注意的是只有当workQueue队列填满时才会创建多于corePoolSize的线程(线程池总线程数不超过maxPoolSize)
  • keepAliveTime非核心线程的空闲时间超过keepAliveTime就会被自动终止回收掉,注意当corePoolSize=maxPoolSize时,keepAliveTime参数也就不起作用了(因为不存在非核心线程);
  • unit:keepAliveTime的时间单位
  • workQueue:用于保存任务的队列,可以为无界、有界、同步移交三种队列类型之一,当池子里的工作线程数大于corePoolSize时,这时新进来的任务会被放到队列中
  • threadFactory:创建线程的工厂类,默认使用Executors.defaultThreadFactory(),也可以使用guava库的ThreadFactoryBuilder来创建
  • handler:线程池无法继续接收任务(队列已满且线程数达到maximunPoolSize)时的饱和策略,取值有AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy
// 默认拒绝策略为 AbortPolicy
ExecutorService threadPool = new ThreadPoolExecutor(10, 20, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100), threadFactory, new ThreadPoolExecutor.AbortPolicy());

追问2:线程池都有哪几种工作队列(WorkQueue)?分别有什么特点?

常用

  • ArrayBlockintQueue(有界队列):基于数组的有限阻塞队列,先进先出(FIFO),插入数据和取出数据公用一个锁对象
  • LinkedBlockingQueue(无界队列):基于链表的无限阻塞队列,先进先出(FIFO),当请求越来越多时(任务处理速度跟不上任务提交速度造成请求堆积)可能导致内存占用过多或OOM,newFixedThreadPool 和 newSingleThreadExecutor 使用这个队列;
  • SynchronousQueue(同步移交队列):不存储元素的阻塞队列,插入操作必须等待一个线程调用移除操作,否则一直会阻塞。newCachedTHreadPool 使用这个队列

不常用

  • PriorityBlockingQueue:有优先级的无限阻塞队列,基于最小二叉堆实现
  • DelayedWorkQueue:每个元素指定延时时间,只有当时间到了才能从队列中获取该元素,是一个无限队列。

课间休息,又来秀一下来自咱们群里同学的搬砖工地,坐标:深圳,美啊。

作者:胡巴


面试题2:线程池提交一个任务经过哪些步骤?

线程池提交任务流程为:

  1. 当线程池新加入一个线程时,首先判断当前线程数,是否小于corePoolSize(核心线程数),如果小于,则执行步骤2,否则执行3;
  2. 创建新线程添加到线程池中,流程结束;
  3. 判断当前线程池等待队列是否已满,若已满,则跳转至步骤5;
  4. 加入等待队列,等待线程池空闲,流程结束;
  5. 判断当前线程数是否已达到maximumPoolSize(最大线程数),若未达到,则跳转至步骤7;
  6. 执行线程池拒绝策略,流程结束;
  7. 创建一个新线程,执行任务;
  8. 流程结束;

  比如现有一个线程池,corePoolSize=10,maxPoolSize=20,队列长度为100,那么当任务过来会先创建10个核心线程数,接下来进来的任务会进入到队列中直到队列满了,会创建额外的线程来执行任务(最多20个线程),这个时候如果再来任务就会执行拒绝策略

追问1:线程池的拒绝策略了解么?

  • AbortPolicy(默认)中断直接抛异常
  • DiscardPolicy:默默丢弃任务,不进行任何通知
  • DiscardOldestPolicy:丢弃掉在队列中存在时间最久的任务
  • CallerRunsPolicy:让提交任务的线程去执行任务

课间休息,来看看我们公司楼下,坐标:北京海淀文教园。

  自从《程序人生,道阻且长》社区开启后,投稿的图片已经用不过来啦,赞。对了,我们会将各薪资价格区间的面试真题加入到社区中,方便大家面试前对标和准备,能正确知道自己有几斤几两。


面试题3:关闭线程池的方式都有哪些?

  • shutdownNow()立即关闭线程池(强制),正在执行中的及队列中的任务会被中断,同时该方法会返回被中断的队列中的任务列表
  • shutdown():平滑关闭线程池,正在执行中的及队列中的任务能执行完成,后续进来的任务会被执行拒绝策略
  • isTerminated():当正在执行的任务及对列中的任务全部都执行完就会返回,return true;

每日小结

  今天我们复习了面试中常问的线程池相关问题,你做到心中有数了么?对了,如果你的朋友也在准备面试,请将这个系列扔给他,如果他认真对待,肯定会感谢你的!!好了,今天就到这里,学废了的同学,记得在评论区留言:打卡。,给同学们以激励。

  看到这里的朋友,我得提前告诉你。

  一个不好的消息:《Java岗大厂面试百日冲刺 - 每日3题》专栏准备改成付费专栏了,时间没定,价格没定。

  原因:我差钱了?是,但不全是。因为发现身边好多朋友关注了《百日冲刺》却从未真正跟上来过,而是感觉再给我捧场,感谢,但不需要。这些朋友们建议默默取关一下,对你我都好。不少同学反应面试中70%以上的问题专栏里出现过,说实话让我为《百日》感到自豪。但一问你又三不知,回来来问我,我只能给你贴个路径。

  讲真,这个专栏花费了博主很多心血,白了不少根头发,虽然很多问题总结的并不好(水平有限),但我是真希望你我能找到理想的工作,对得起自己。好了,就到这。

  对了,近期准备安排一个专门讨论面试的小群。后面我在告诉你吧。

以上是关于Java岗大厂面试百日冲刺Day47— 并发编程4(日积月累,每日三题)的主要内容,如果未能解决你的问题,请参考以下文章

Java岗大厂面试百日冲刺 - 日积月累,每日三题Day22—— 并发编程2

Java岗大厂面试百日冲刺 - 日积月累,每日三题Day37—— 并发编程3

Java岗大厂面试百日冲刺 - 日积月累,每日三题Day10 —— 并发编程1

Java岗大厂面试百日冲刺 - 日积月累,每日三题Day13—— 网络编程2

Java岗大厂面试百日冲刺Day53— 基础篇4 (日积月累,每日三题)

Java岗大厂面试百日冲刺Day53— 基础篇4 (日积月累,每日三题)