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

网友投稿 742 2022-05-29

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

Java多线程

1.先看问题

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

线程池的原理,为什么要创建线程池?创建线程池的方式;

线程的生命周期,什么时候会出现僵死进程;

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

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

volatile、ThreadLocal的使用场景和原理;

ThreadLocal什么时候会出现OOM的情况?为什么?

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:线程池参数问题

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

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

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

1

2

3

4

5

6

7

8

9

10

相关参数介绍:

当线程池中线程数量小于 corePoolSize 则创建线程,并处理请求。

当线程池中线程数量大于等于 corePoolSize 时,则把请求放入 workQueue 中,随着线程池 中的核心线程们不断执行任务,只要线程池中有空闲的核心线程,线程池就从 workQueue 中取 任务并处理。

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

当 workQueue 已存满,放不下新任务时则新建非核心线程入池,并处理请求直到线程数目 达到 maximumPoolSize(最大线程数量设置值)。

如果线程池中线程数大于 maximumPoolSize 则使用 RejectedExecutionHandler 来进行任 务拒绝处理。

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

然后我们需要来看先线程池的大小分配了。

线程池究竟设置多大要看你的线程池执行的什么任务了,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变量进行写操作时,JVM 会向处理器发送一条 Lock 前缀的指令,将这个变量所在缓 存行的数据写会到系统内存。

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

volatile的适用场景:

状态标志,如:初始化或请求停机

一次性安全发布,如:单列模式

独立观察,如:定期更新某个值

“volatile bean” 模式

开销较低的“读-写锁”策略,如:计数器

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有什么区别

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

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

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

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

Java 任务调度 多线程

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Linux多线程-互斥和同步
下一篇:[跟着官方文档学Junit5][一][Overview][学习笔记]
相关文章