BAT大厂面试必问专题之Java多线程

Posted 波波烤鸭

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BAT大厂面试必问专题之Java多线程相关的知识,希望对你有一定的参考价值。

  根据众多面试的同学反馈的面试题,给大家整理一版最新的面试专题,希望对大家有所帮助。

Java多线程

1.先看问题

  先来看看经常会问到的问题。

  1. 线程池的原理,为什么要创建线程池?创建线程池的方式;
  2. 线程的生命周期,什么时候会出现僵死进程;
  3. 说说线程安全问题,什么实现线程安全,如何实现线程安全;
  4. 创建线程池有哪几个核心参数? 如何合理配置线程池的大小?
  5. volatile、ThreadLocal的使用场景和原理;
  6. ThreadLocal什么时候会出现OOM的情况?为什么?
  7. synchronized、volatile区别、synchronized锁粒度、模拟死锁场景、原子性与可见性;

看看你能回答几个哦。

然后我们再来看看每个问题我们应该怎么来回答吧!

2.再看答案

2.1 Q1:线程池的原理

  线程池的原理,为什么要创建线程池?创建线程池的方式有哪些?
原理和创建线程池的实现请我之前整理的这篇文章:
Java线程池原理讲解
创建线程池的几种方式:

  • ThreadPoolExecutor
  • ThreadScheduledExecutor
  • ForkJoinPool

2.1 Q2:线程的生命周期

  线程的生命周期,什么时候会出现僵死进程;
  僵死进程是指子进程退出时,父进程并未对其发出的SIGCHLD信号进行适当处理,导致子 进程停留在僵死状态等待其父进程为其收尸,这个状态下的子进程就是僵死进程。

2.3 Q3:线程安全问题

说说线程安全问题,什么是线程安全,如何实现线程安全;

  • 线程安全 - 如果线程执行过程中不会产生共享资源的冲突,则线程安全。
  • 线程不安全 - 如果有多个线程同时在操作主内存中的变量,则线程不安全

实现线程安全的三种方式

1)互斥同步

  • 临界区:syncronized、ReentrantLock
  • 信号量 semaphore
  • 互斥量 mutex

2)非阻塞同步

  • CAS(Compare And Swap)

3)无同步方案

  • 可重入代码
  • 使用Threadlocal 类来包装共享变量,做到每个线程有自己的copy
  • 线程本地存储

多线程的安全机制:数据安全机制

2.4 Q4:线程池参数问题

创建线程池有哪几个核心参数? 如何合理配置线程池的大小?

1.核心参数

首先我们来看下核心参数:

public ThreadPoolExecutor(
			int corePoolSize,  // 核心线程数量大小   
			int maximumPoolSize, // 线程池最大容纳线程数
			long keepAliveTime, // 线程空闲后的存活时长
			TimeUnit unit,
			//缓存异步任务的队列 //用来构造线程池里的worker线程
			BlockingQueue<Runnable> workQueue,
			ThreadFactory threadFactory,
			//线程池任务满载后采取的任务拒绝策略 
			RejectedExecutionHandler handler)

2.参数说明

相关参数介绍:

  1. 当线程池中线程数量小于 corePoolSize 则创建线程,并处理请求。
  2. 当线程池中线程数量大于等于 corePoolSize 时,则把请求放入 workQueue 中,随着线程池 中的核心线程们不断执行任务,只要线程池中有空闲的核心线程,线程池就从 workQueue 中取 任务并处理。
  3. 当 workQueue 已存满,放不下新任务时则新建非核心线程入池,并处理请求直到线程数目 达到 maximumPoolSize(最大线程数量设置值)。
  4. 如果线程池中线程数大于 maximumPoolSize 则使用 RejectedExecutionHandler 来进行任 务拒绝处理。

具体可以参考本文的详解:线程池核心配置讲解

3.线程池大小

  然后我们需要来看先线程池的大小分配了。
  线程池究竟设置多大要看你的线程池执行的什么任务了,CPU密集型、IO密集型、混合型,任 务类型不同,设置的方式也不一样。

  任务一般分为:CPU密集型、IO密集型、混合型,对于不同类型的任务需要分配不同大小的线程池。

3.1 CPU密集型

  尽量使用较小的线程池,一般Cpu核心数+1

3.2 IO密集型

  • 方法一:可以使用较大的线程池,一般CPU核心数 * 2 IO密集型CPU使用率不高,可以让CPU等待IO的时候处理别的任务,充分利用cpu时间

  • 方法二:(线程等待时间与线程CPU时间之比 + 1)* CPU数目
    下面举个例子:
    比如平均每个线程CPU运行时间为0.5s,而线程等待时间(非CPU运行时间,比如IO)为1.5s,CPU核心数为8,那么根据上面这个公式估算得到:((0.5+1.5)/0.5)*8=32。这个公式进一步转化为:

最佳线程数目 = (线程等待时间与线程CPU时间之比 + 1)* CPU数目

3.3 混合型
  可以将任务分为CPU密集型和IO密集型,然后分别使用不同的线程池去处理,按情况而定

2.5 Q5:volatile和ThreadLocal

问题:volatile、ThreadLocal的使用场景和原理?

volatile原理

  volatile变量进行写操作时,JVM 会向处理器发送一条 Lock 前缀的指令,将这个变量所在缓 存行的数据写会到系统内存。

  Lock 前缀指令实际上相当于一个内存屏障(也成内存栅栏),它确保指令重排序时不会把其 后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内 存屏障这句指令时,在它前面的操作已经全部完成。

volatile的适用场景:

  1. 状态标志,如:初始化或请求停机
  2. 一次性安全发布,如:单列模式
  3. 独立观察,如:定期更新某个值
  4. “volatile bean” 模式
  5. 开销较低的“读-写锁”策略,如:计数器

ThreadLocal原理

  ThreadLocal是用来维护本线程的变量的,并不能解决共享变量的并发问题。ThreadLocal是 各线程将值存入该线程的map中,以ThreadLocal自身作为key,需要用时获得的是该线程之前 存入的值。如果存入的是共享变量,那取出的也是共享变量,并发问题还是存在的。

ThreadLocal的适用场景

场景:数据库连接、Session管理

2.6 Q6:OOM情况

问题:ThreadLocal什么时候会出现OOM的情况?为什么?
  ThreadLocal变量是维护在Thread内部的,这样的话只要我们的线程不退出,对象的引用就会 一直存在。当线程退出时,Thread类会进行一些清理工作,其中就包含ThreadLocalMap, Thread调用exit方法如下:

&esmp; ThreadLocal在没有线程池使用的情况下,正常情况下不会存在内存泄露,但是如果使用了线程 池的话,就依赖于线程池的实现,如果线程池不销毁线程的话,那么就会存在内存泄露。

2.7 Q7:synchronized、volatile区别

问题:synchronized、volatile有什么区别

  1. volatile主要应用在多个线程对实例变量更改的场合,刷新主内存共享变量的值从而使得各个 线程可以获得最新的值,线程读取变量的值需要从主存中读取;synchronized则是锁定当前变 量,只有当前线程可以访问该变量,其他线程被阻塞住。另外,synchronized还会创建一个内 存屏障,内存屏障指令保证了所有CPU操作结果都会直接刷到主存中(即释放锁前),从而保证 了操作的内存可见性,同时也使得先获得这个锁的线程的所有操作

  2. volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的。 volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞,比如多个线程争抢 synchronized锁对象时,会出现阻塞。

  3. volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的 修改可见性和原子性,因为线程获得锁才能进入临界区,从而保证临界区中的所有语句全部得到 执行。

  4. volatile标记的变量不会被编译器优化,可以禁止进行指令重排;synchronized标记的变量 可以被编译器优化。

以上是关于BAT大厂面试必问专题之Java多线程的主要内容,如果未能解决你的问题,请参考以下文章

读书人的事情怎么能算"偷"呢?某大厂开发者对于Android多线程的总结

最全的BAT大厂面试题整理!java多线程导入excel数据

详解BAT面试必问之ThreadLocal(源码+内存)

《我想进大厂》之MQ夺命连环11问

2021大厂Java面试必问题目,爱了爱了

Python | 面试必问,线程与进程的区别,Python中如何创建多线程?