一、前言
本文涉及的六个问题,全部出自于博主在大厂面试,所遇到问题。
问题本身不难,都是些基础的概念,这些问题的价值在于每一个问题背后都可以挖出很多要点。
这些问题相当于是一个垫脚石,基本如果这些问题都回答不好,那么问深层次八股文的机会都不会有,面试必挂。
所以大家请耐心看完,已经会的就当是温故知新了。
当然我说的有错或者纰漏也欢迎评论指正。
二、并发干货,非常干
题目 01- 请你说一说什么是线程和进程?
- 区别 进程:有独立内存空间,每个进程中的数据空间都是独立的。
线程:多线程之间堆空间与方法区是共享的,但每个线程的栈空间、程序计数器是独立的,线程消耗的资源比进程小的多。
- 关系 进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡过程。
一个进程可以有一个或者多个线程组成;线程是是进程中的一个执行单元,负责当前进程中任务的执行。
- 线程任务状态的保存及再加载的过程就叫做上下文切换。
- 线程的并发与并行有啥区别? 并发(Concurrent):同一时间段内,多个任务都会被执行到 ,但不是在单位时间内同时执行。
并行(Parallel):单位时间内多个任务同时执行,并行上限取决于CPU核数。
题目 02- 使用了多线程会带来什么问题呢?
- 能不能详细说说线程安全问题 线程安全问题出现在对同一块内存地址值的访问,如果这块内存地址值永远都不会改变,那么就不会出现线程安全问题。相反这个值,如果会被改变,那么多个线程就要考虑一个问题,大家由于上下文切换的缘故,在轮到其中一个线程访问该值的时候,是否读到的值和其他线程是同一个,线程安全问题由此产生。
- 原子性、有序性和可见性能不能深入的谈一下
➢ 原子性:指一组操作是一个完整的个体,要不都执行,要不不执行;
➢ 有序性:写程序是按照一定顺序,但程序在执行过程中由于指令重排,执行顺序会被调整;
➢ 可见性:仅在多线程有读写的情况需要考虑,一个线程修改内存中某一个值时,需要保证其他线程能读到修改的值,而不是原值。
题目 03- 什么是死锁?如何排查死锁?
- 排查过程最好详细说明,最少说一种排查方案,越多越好 出现死锁,使用Arthas连接,对应JVM实例,thread -b命令一键式检测死锁。
当然这边还能提一嘴,JVM自带的jstack命令。
题目 04- 请你说一说 JMM 与 happens-before ?
这道题坑是比较大的,首先一点对于刚接触Java的程序员来说,可能还有一个JVM内存模型八股文被广泛传颂,但是注意一点你说了它,那就尬住,
- 什么是 JMM 内存模型? JMM定义是Java语言与操作系统内存是如何交互;而JVM内存模型,指的是JVM实例运行时内存区域是如何划分的。
其实这点你能明确讲到,其他的深入再说说,不同系统有不同的实现细节等等,这些就都是锦上添花了,能衍生自己擅长的那就是最好的。
- 什么是 happens-before 规则? 涉及的规则不止我下面罗列的,但是以下四条是我认为比较关键的点,且大家的说法也基本都是如出一辙,这里我就拿相关资料上所描述的内容,
➢ 程序次序规则:在一个线程中,按照代码的顺序,前面的操作happens-before于后面的任意操作
➢ Volatile变量规则:对一个volatile修饰的变量的写, happens-before与任意后续对这个变量的读
➢ 传递性:如果A happens-before B,并且B happens-before C,则A happens-before C
➢ 锁定规则:对一个锁的解锁, happens-before与随后对这个锁的加锁
如果能能再展开讲讲线程启动规则、线程终结规则、线程中断规则、对象终结原则,那很不错。
题目 05- 为什么使用线程池?如何创建线程池?
- 手动创建和自动创建线程池都需要介绍 手动创建,使用ThreadPoolExecutor的构造函数,配置线程池参数。
自动创建,使用Executors提供的几个类方法直接创建。
- 最好介绍一下线程池的实现原理 由于频繁创建线程是会有额外的时间开销的,因此池化的理念同样被JDK中线程池引入并实现。
这里重点关注两个参数,corePoolSize、maxPoolSize,参数的关系如下所述,
➢线程数 < corePoolSize,就算有工作线程处于空闲状态,仍旧会创建新线程执行task。
➢线程数 >= corePoolSize,但 < maxPoolSize,将任务放入task队列中。
➢队列已满,并且线程数 < maxPoolSize,那么创建新线程来执行task。
➢队列已满,并且线程数 >= maxPoolSize,则根据任务拒绝策略进行拒绝。
题目 06-ThreadLocal 中 Map 的 key 为什么要使用弱引用?
- 为什么说不清理自定义的 ThreadLocal 变量会导致内存泄露呢? 相互之间的关系,这块最好能够手绘出来,这样既体现你对其的理解,一定程度上也能够体现你代码设计能力。
Thread Ref → Current Thread → ThreadLocalMap → Entry → Value
线程对象存活以上链路的引用关系就会一直存在,如果该线程执行耗时任务,线程一直存活,那么当GC进行可达性分析的时候,由于 Value 可达的,无法回收。 如果业务逻辑已经完成处理,这时就不再需要这个 Value,但是由于其一直未被清理,此时也就发生了内存泄漏问题。