【秋招必备】多线程面试题(2021最新版)
前言
现在不管是大公司还是小公司,去面试都会问到多线程与并发编程的知识,大家面试的时候这方面的知识一定要提前做好储备。
小编分享的这份Java后端开发面试总结包含了JavaOOP、Java集合容器、Java异常、并发编程、Java反射、Java序列化、JVM、Redis、Spring MVC、MyBatis、MySQL数据库、消息中间件MQ、Dubbo、Linux、ZooKeeper、 分布式&数据结构与算法等26个专题技术点,都是小编在各个大厂总结出来的面试真题,已经有很多粉丝靠这份PDF拿下众多大厂的offer,今天在这里总结分享给到大家!【已完结】
完整版Java面试题地址:2021最新面试题合集集锦。
1.多线程有什么用?
1)发挥多核CPU 的优势
随着工业的进步,现在的笔记本、台式机乃至商用的应用服务器至少也都是双核的,4 核、8 核甚至 16 核的也都不少见,如果是单线程的程序,那么在双核 CPU 上 就浪费了 50%, 在 4 核 CPU 上就浪费了 75%。单核 CPU 上所谓的"多线程"那是假的多线程,同一时间处理器只会处理一段逻辑,只不过线程之间切换得比较快, 看着像多个线程"同时"运行罢了。多核 CPU 上的多线程才是真正的多线程,它能让你的多段逻辑同时工作,多线程,可以真正发挥出多核CPU 的优势来,达到充分利用CPU 的目的。
2)防止阻塞
从程序运行效率的角度来看,单核 CPU 不但不会发挥出多线程的优势,反而会因为在单核CPU 上运行多线程导致线程上下文的切换,而降低程序整体的效率。但
是单核 CPU 我们还是要应用多线程,就是为了防止阻塞。试想,如果单核 CPU 使用单线程,那么只要这个线程阻塞了,比方说远程读取某个数据吧,对端迟迟未返回又没有设置超时时间,那么你的整个程序在数据返回回来之前就停止运行了。多线程可以防止这个问题,多条线程同时运行,哪怕一条线程的代码执行读取数据阻塞,也不会影响其它任务的执行。
3)便于建模
这是另外一个没有这么明显的优点了。假设有一个大的任务 A,单线程编程,那么就要考虑很多,建立整个程序模型比较麻烦。但是如果把这个大的任务 A 分解成几个小任务,任务B、任务 C、任务 D,分别建立程序模型,并通过多线程分别运行这几个任务,那就简单很多了。
2.线程和进程的区别是什么?
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
3.Java 实现线程有哪几种方式?
- 1)继承 Thread 类实现多线程
- 2)实现 Runnable 接口方式实现多线程
- 3)使用 ExecutorService、Callable、Future 实现有返回结果的多线程
4.启动线程方法 start()和 run()有什么区别?
只有调用了 start()方法,才会表现出多线程的特性,不同线程的 run()方法里面的代码交替执行。如果只是调用 run()方法,那么代码还是同步执行的,必须等待一个线程的 run()方法里面的代码全部执行完毕之后,另外一个线程才可以执行其 run()方法里面的代码。
5.怎么终止一个线程?如何优雅地终止线程?
stop 终止,不推荐。
6.一个线程的生命周期有哪几种状态?它们之间如何流转的?
NEW:毫无疑问表示的是刚创建的线程,还没有开始启动。
RUNNABLE: 表示线程已经触发 start()方式调用,线程正式启动,线程处于运行中状态。
BLOCKED:表示线程阻塞,等待获取锁,如碰到 synchronized、lock 等关键字等占用临界区的情况,一旦获取到锁就进行 RUNNABLE 状态继续运行。
WAITING:表示线程处于无限制等待状态,等待一个特殊的事件来重新唤醒,如通过wait()方法进行等待的线程等待一个 notify()或者 notifyAll()方法,通过 join()方法进行等待的线程等待目标线程运行结束而唤醒,一旦通过相关事件唤醒线程,线程就进入了 RUNNABLE 状态继续运行。
TIMED_WAITING:表示线程进入了一个有时限的等待,如 sleep(3000),等待 3 秒后线程重新进行 RUNNABLE 状态继续运行。
TERMINATED:表示线程执行完毕后,进行终止状态。需要注意的是,一旦线程通过 start 方法启动后就再也不能回到初始 NEW 状态,线程终止后也不能再回到 RUNNABLE 状态
7.线程中的 wait()和 sleep()方法有什么区别?
这个问题常问,sleep 方法和 wait 方法都可以用来放弃 CPU 一定的时间,不同点在于如果线程持有某个对象的监视器,sleep 方法不会放弃这个对象的监视器,wait 方法会放弃这个对象的监视器
8.多线程同步有哪几种方法?
Synchronized 关键字,Lock 锁实现,分布式锁等。
9.什么是死锁?如何避免死锁?
死锁就是两个线程相互等待对方释放对象锁。
10.多线程之间如何进行通信?
wait/notify
11、线程怎样拿到返回结果?
实现Callable 接口。
12、violatile 关键字的作用?
13、新建 T1、T2、T3 三个线程,如何保证它们按顺序执行?
用 join 方法。
14、怎么控制同一时间只有 3 个线程运行?
用 Semaphore。
15、为什么要使用线程池?
我们知道不用线程池的话,每个线程都要通过 new Thread(xxRunnable).start()的方式来创建并运行一个线程,线程少的话这不会是问题,而真实环境可能会开启多个1线程让系统和程序达到最佳效率,当线程数达到一定数量就会耗尽系统的 CPU 和内存资源,也会造成 GC频繁收集和停顿,因为每次创建和销毁一个线程都是要消 耗系统资源的,如果为每个任务都创建线程这无疑是一个很大的性能瓶颈。所以,线程池中的线程复用极大节省了系统资源,当线程一段时间不再有任务处理时它也 会自动销毁,而不会长驻内存。
16、常用的几种线程池并讲讲其中的工作原理。
17、线程池启动线程 submit()和 execute()方法有什么不同?
18、CyclicBarrier 和 CountDownLatch 的区别?
两个看上去有点像的类,都在 java.util.concurrent 下,都可以用来表示代码运行到某个点上,二者的区别在于:
1.CyclicBarrier 的某个线程运行到某个点上之后,该线程即停止运行,直到所有的线程都到达了这个点,所有线程才重新运行;CountDownLatch 则不是,某线程运 行到某个点上之后,只是给某个数值-1 而已,该线程继续运行1
2.CyclicBarrier 只能唤起一个任务,CountDownLatch 可以唤起多个任务
3.CyclicBarrier 可 重 用 , CountDownLatch 不 可 重 用 , 计 数 值 为 0 该CountDownLatch就不可再用了
19、什么是活锁、饥饿、无锁、死锁?
20、什么是原子性、可见性、有序性?
21、什么是守护线程?有什么用?
什么是守护线程?与守护线程相对应的就是用户线程,守护线程就是守护用户线程,当用户线程全部执行完结束之后,守护线程才会跟着结束。也就是守护线程必 须伴随着用户线程,如果一个应用内只存在一个守护线程,没有用户线程,守护线程自然会退出。
22、一个线程运行时发生异常会怎样?
如果异常没有被捕获该线程将会停止执行。Thread.UncaughtExceptionHandler 是用于处理未捕获异常造成线程突然中断情况的一个内嵌接口。当一个未捕获异常将造 成线程中断的时 候 JVM 会 使 用 Thread.getUncaughtExceptionHandler() 来 查 询 线程 的UncaughtExceptionHandler 并 将 线 程 和 异 常 作 为 参 数 传 递 给 handler 的 uncaughtException()方法进行处理。
23、线程 yield()方法有什么用?
Yield 方法可以暂停当前正在执行的线程对象,让其它有相同优先级的线程执行。它是一个静态方法而且只保证当前线程放弃 CPU 占用而不能保证使其它线程一定 能占用 CPU,执行yield()的线程有可能在进入到暂停状态后马上又被执行。
24、什么是重入锁?
所谓重入锁,指的是以线程为单位,当一个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,而其他的线程是不可以的。
25、Synchronized 有哪几种用法?
锁类、锁方法、锁代码块
26、Fork/Join 框架是干什么的?
大任务自动分散小任务,并发执行,合并小任务结果。
27、线程数过多会造成什么异常?
线程过多会造成栈溢出,也有可能会造成堆异常
28、说说线程安全的和不安全的集合。
Java 中平时用的最多的 Map 集合就是 HashMap 了,它是线程不安全的。 看下面两个场景:
1、当用在方法内的局部变量时,局部变量属于当前线程级别的变量,其他线程访问不了,所以这时也不存在线程安全不安全的问题了。1
2、当用在单例对象成员变量的时候呢?这时候多个线程过来访问的就是同一个HashMap 了,对同个 HashMap 操作这时候就存在线程安全的问题了。
29、什么是 CAS 算法?在多线程中有哪些应用。
30、怎么检测一个线程是否拥有锁?
java.lang.Thread#holdsLock 方法
31、Jdk 中排查多线程问题用什么命令?
jstack
32、线程同步需要注意什么?
1、尽量缩小同步的范围,增加系统吞吐量。
2、分布式同步锁无意义,要使用分布式锁。
3、防止死锁,注意加锁顺序。
33、线程 wait()方法使用有什么前提?
要在同步块中使用。
34、Fork/Join 框架使用有哪些要注意的地方?
如果任务拆解的很深,系统内的线程数量堆积,导致系统性能性能严重下降;如果函数的调用栈很深,会导致栈内存溢出;
35、线程之间如何传递数据?
36、保证"可见性"有哪几种方式?
synchronized 和 viotatile
37、说几个常用的 Lock 接口实现锁。
ReentrantLock、ReadWriteLock
38、ThreadLocal 是什么?有什么应用场景?
ThreadLocal 的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。用来解决数据库连接、Session 管理等。
39、ReadWriteLock 有什么用?
ReadWriteLock 是一个读写锁接口,ReentrantReadWriteLock 是 ReadWriteLock 接口的一个具体实现,实现了读写的分离,读锁是共享的,写锁是独占的,读和读之间不会互斥,读和写、写和读、写和写之间才会互斥,提升了读写的性能。
40、FutureTask 是什么?
FutureTask 表示一个异步运算的任务,FutureTask 里面可以传入一个 Callable 的具1体实现类,可以对这个异步运算的任务的结果进行等待获取、判断是否已经完成、取消任务等操作。
41、怎么唤醒一个阻塞的线程?
42、不可变对象对多线程有什么帮助?
43、多线程上下文切换是什么意思?
44、Java 中用到了什么线程调度算法?
45、Thread.sleep(0)的作用是什么?
46、Java 内存模型是什么,哪些区域是线程共享的,哪些是不共享的?
47、什么是乐观锁和悲观锁?
48、Hashtable 的 size()方法为什么要做同步?
49、同步方法和同步块,哪种更好?
50、什么是自旋锁?
自旋锁是采用让当前线程不停地的在循环体内执行实现的,当循环的条件被其他线程改变时才能进入临界区。
51、Runnable 和 Thread 用哪个好?
Java 不支持类的多重继承,但允许你实现多个接口。所以如果你要继承其他类,也为了减少类之间的耦合性,Runnable 会更好。
52、Java 中 notify 和 notifyAll 有什么区别?
notify()方法不能唤醒某个具体的线程,所以只有一个线程在等待的时候它才有用武之地。
而 notifyAll()唤醒所有线程并允许他们争夺锁确保了至少有一个线程能继续运行。
53、为什么 wait/notify/notifyAll 这些方法不在 thread 类里面?
54、为什么 wait 和 notify 方法要在同步块中调用?
55、为什么你应该在循环中检查等待条件?
56、Java 中堆和栈有什么不同?
57、你如何在 Java 中获取线程堆栈?
58、如何创建线程安全的单例模式?
59、什么是阻塞式方法?
阻塞式方法是指程序会一直等待该方法完成期间不做其他事情,ServerSocket 的accept()方法就是一直等待客户端连接。这里的阻塞是指调用结果返回之前,当前
线程会被挂起,直到得到结果之后才会返回。此外,还有异步和非阻塞式方法在任务完成前就返回。
60、提交任务时线程池队列已满会时发会生什么?
当线程数小于最大线程池数 maximumPoolSize 时就会创建新线程来处理,而线程数大于等于最大线程池数 maximumPoolSize 时就会执行拒绝策略。