面试题|【Java实习生面试题系列】-- 多线程篇四


文章目录

    • 1. 说下对同步器 AQS 的理解?
    • 2. AQS 的原理是什么?
    • 3. AQS 底层使用了模板方法模式,你能说出几个需要重写的方法吗?
    • 4. 说下对 Semaphore、CountDownLatch 和 CyclicBarrier 的理解?
    • 5. 说下对线程池的理解?为什么要使用线程池?
    • 6. 创建线程池的参数有哪些?
    • 7. 如何合理配置线程池参数?
    • 8. 执行 execute() 方法和 submit() 方法的区别是什么呢?
    • 9. 说下对 Fork和Join 并行计算框架的理解?

  • Day3,第四篇
  • 本文章的主题是 【Java实习生面试题系列】-- 多线程篇四
面试题|【Java实习生面试题系列】-- 多线程篇四
文章图片

1. 说下对同步器 AQS 的理解? Java 中的大部分同步类(Lock、Semaphore、ReentrantLock等)都是基于 AbstractQueuedSynchronizer(简称为 AQS)实现的。 AQS 是一种提供了原子式管理同步状态、阻塞和唤醒线程功能以及队列模型的简单框架。
AQS 中的锁类型有两种:分别是 「Exclusive(独占锁)] 和 [Share(共享锁)」。
「独占锁」就是「每次都只有一个线程运行」,例如 ReentrantLock
「共享锁」就是「同时可以多个线程运行」,如 Semaphore、CountDownLatch、ReentrantReadWriteLock
2. AQS 的原理是什么? 【面试题|【Java实习生面试题系列】-- 多线程篇四】AQS 核心思想是,如果被请求的共享资源空闲,那么就将当前请求资源的线程设置为有效的工作线程,将共享资源设置为锁定状态;如果共享资源被占用,则调用 LockSupport().park()方法将Node中的线程状态改为WAITING,等待被唤醒或被中断 ,就需要一定的阻塞等待唤醒机制来保证锁分配。这个机制主要用的是 CLH 队列的变体实现的,将暂时获取不到锁的线程加入到队列中。
CLH:Craig、Landin and Hagersten队列,是单向链表,AQS中的队列是CLH变体的虚拟双向队列(FIFO), AQS 是通过将每条请求共享资源的线程封装成一个节点来实现锁的分配。
主要原理图如下:
面试题|【Java实习生面试题系列】-- 多线程篇四
文章图片

  • AQS 使用一个 Volatileint 类型的成员变量来表示同步状态,通过内置的FIFO队列来完成资源获取的排队工作,通过 CAS 完成对 State 值的修改。
  • FIFO 队列中,「头节点占有锁」,也就是头节点才是锁的持有者,尾指针指向队列的最后一个等待线程节点,除了头节点和尾节点,节点之间都有 「前驱指针」 和 「后继指针」
  • AQS 中维护了一个 「共享变量state」,标识当前的资源是否被线程持有,多线程竞争的时候,会去判断 state 是否为 0 ,尝试的去把 state 修改为 1
3. AQS 底层使用了模板方法模式,你能说出几个需要重写的方法吗?
  • 独占方式尝试获取资源的方法 tryAcquire
  • 独占方式尝试释放资源的方法 tryRelease
  • 共享方式尝试获取资源的方法 tryAcquireShared
  • 共享方式尝试释放资源的方法 tryReleaseShared
4. 说下对 Semaphore、CountDownLatch 和 CyclicBarrier 的理解?
  • Semaphore(信号量)-允许多个线程同时访问: synchronizedReentrantLock 都是一次只允许一个线程访问某个资源,Semaphore(信号量)可以指定多个线程同时访问某个资源。
  • CountDownLatch (倒计时器): CountDownLatch 是一个同步工具类,用来协调多个线程之间的同步。这个工具通常用来控制线程等待,它可以让某一个线程等待直到倒计时结束,再开始执行。
  • CyclicBarrier(循环栅栏): CyclicBarrierCountDownLatch 非常类似,它也可以实现线程间的技术等待,但是它的功能比 CountDownLatch 更加复杂和强大。主要应用场景和 CountDownLatch 类似。 CyclicBarrier 的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。 CyclicBarrier 默认的构造方法是 CyclicBarrier (int parties),其参数表示屏障拦截的线程数量,每个线程调用 await 方法告诉 CyclicBarrier 我已经到达了屏障,然后当前线程被阻塞。
5. 说下对线程池的理解?为什么要使用线程池? 线程池提供了一种限制和管理资源(包括执行一个任务)。 每个线程池还维护一些基本统计信息,例如已完成任务的数量。
使用线程池的好处:
  • 降低资源消耗: 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  • 提高响应速度: 当任务到达时,任务可以不需要的等到线程创建就能立即执行。
  • 提高线程的可管理性: 线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
6. 创建线程池的参数有哪些?
  • corePoolSize : 核心线程大小。线程池一直运行,核心线程就不会停止。
  • maximumPoolSize :线程池最大线程数量。非核心线程数量=maximumPoolSize-corePoolSize
  • keepAliveTime :非核心线程的心跳时间。如果非核心线程在keepAliveTime内没有运行任务,非核心线程会消亡。
  • workQueue :阻塞队列。ArrayBlockingQueue,LinkedBlockingQueue等,用来存放线程任务。
  • defaultHandler :饱和策略。ThreadPoolExecutor类中一共有4种饱和策略。通过实现 RejectedExecutionHandler 接口。
    • AbortPolicy : 线程任务丢弃报错。默认饱和策略。
    • DiscardPolicy : 线程任务直接丢弃不报错。
    • DiscardOldestPolicy : 将workQueue队首任务丢弃,将最新线程任务重新加入队列执行。
    • CallerRunsPolicy :线程池之外的线程直接调用run方法执行。
  • ThreadFactory :线程工厂。新建线程工厂。
7. 如何合理配置线程池参数? 自定义线程池就需要我们自己配置最大线程数 maximumPoolSize ,为了高效的并发运行,这时需要看我们的业务是 IO密集型还是CPU密集型
  • CPU密集型
CPU密集的意思是该任务需要最大的运算,而没有阻塞,CPU一直全速运行。CPU密集任务只有在真正的多核CPU上才能得到加速(通过多线程)。而在单核CPU上,无论你开几个模拟的多线程该任务都不可能得到加速,因为CPU总的运算能力就那么多。
  • IO密集型
IO密集型,即该任务需要大量的IO,即大量的阻塞。在单线程上运行IO密集型的任务会导致大量的CPU运算能力浪费在等待。所以在IO密集型任务中使用多线程可以大大的加速程序运行,即使在单核CPU上这种加速主要就是利用了被浪费掉的阻塞时间。
IO 密集型时,大部分线程都阻塞,故需要多配制线程数。公式为:
CPU核数*2 CPU核数/(1-阻塞系数) 阻塞系数在0.8~0.9之间 查看CPU核数: System.out.println(Runtime.getRuntime().availableProcessors());

当以上都不适用时,选用动态化线程池,看美团技术团队的实践
8. 执行 execute() 方法和 submit() 方法的区别是什么呢?
  • execute() 方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否;
  • submit()方法用于提交需要返回值的任务。线程池会返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的 get() 方法来获取返回值,get()方法会阻塞当前线程直到任务完成,而使用 get(long timeout,TimeUnit unit) 方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。
9. 说下对 Fork和Join 并行计算框架的理解? Fork/Join 框架是 Java7 提供的一个用于并行执行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。
Fork/Join 框架需要理解两个点,「分而治之」和「工作窃取算法」。
「分而治之」
以上 Fork/Join 框架的定义,就是分而治之思想的体现啦
面试题|【Java实习生面试题系列】-- 多线程篇四
文章图片

「工作窃取算法」
把大任务拆分成小任务,放到不同队列执行,交由不同的线程分别执行时。有的线程优先把自己负责的任务执行完了,其他线程还在慢慢悠悠处理自己的任务,这时候为了充分提高效率,就需要工作盗窃算法啦~
面试题|【Java实习生面试题系列】-- 多线程篇四
文章图片

今天的面试题就总结这么一些吧,总结面试题也花费了我不少时间,所以说总结不易,如果你感觉对你有帮助的话,请你三连支持,后面的文章会一点点更新。祝大家 offer 连连!!!
面试题|【Java实习生面试题系列】-- 多线程篇四
文章图片

    推荐阅读