面试题由23年8月开始记录,主要参考B站黑马程序员的视频(新版Java面试专题视频教程,java八股文面试全套真题+深度详解(含大厂高频面试真题)_哔哩哔哩_bilibili)和JavaGuide(项目介绍 | JavaGuide),并在多次面试中二次改进并增加题目。内容仅供参考,建议理解使用。
多线程(基础频率高,问过线程池)
基础知识
线程和进程的区别?
建议自己理解并举例回答,如举例微信与公众号的关系(微信为进程,公众号为线程)
并行和并发有什么区别?
建议自己理解并举例回答
创建线程的方式有哪些?(大概率问)
- 继承Thread类
- 实现runable接口
- 实现Callable接口
- 线程池创建
- 使用匿名内部类(本质上也是上面四种)
-
继承Thread类: 创建一个类继承自Thread类,并重写其
run()
方法。然后创建这个类的实例,并调用其start()
方法来启动线程。public class MyThread extends Thread { @Override public void run() { // 线程要执行的代码 } } // 在其他类中使用 MyThread myThread = new MyThread(); myThread.start();
-
实现Runnable接口: 创建一个类实现Runnable接口,并实现其
run()
方法。然后将Runnable实例传递给Thread对象,最后调用Thread对象的start()
方法。public class MyRunnable implements Runnable { @Override public void run() { // 线程要执行的代码 } } // 在其他类中使用 Thread thread = new Thread(new MyRunnable()); thread.start();
-
实现Callable接口: 与Runnable类似,但Callable允许任务返回值,并且可以抛出异常。Callable任务需要借助
FutureTask
包装器来创建Thread。import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; public class MyCallable implements Callable<Integer> { @Override public Integer call() throws Exception { // 线程要执行的代码,并返回结果 return 123; } } // 在其他类中使用 FutureTask<Integer> futureTask = new FutureTask<>(new MyCallable()); Thread thread = new Thread(futureTask); thread.start(); // 获取任务结果 Integer result = futureTask.get();
-
使用Executor框架: Java 5引入了基于固定池大小的线程池的
Executors
类,可以创建不同类型的线程池来管理线程。使用Executors
可以简化线程的创建和管理。import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; // 创建一个固定大小的线程池 ExecutorService executor = Executors.newFixedThreadPool(10); // 提交任务 executor.execute(new MyRunnable()); // 关闭线程池 executor.shutdown();
-
使用匿名内部类: 在某些情况下,可以使用匿名内部类来快速创建线程,特别是当线程只需要执行一次任务时。
// 使用Thread的匿名内部类 new Thread(new Runnable() { @Override public void run() { // 线程要执行的代码 } }).start(); // 或者使用Callable的匿名内部类 FutureTask<Integer> futureTask = new FutureTask<>(new Callable<Integer>() { @Override public Integer call() throws Exception { // 线程要执行的代码,并返回结果 return 123; } }); Thread thread = new Thread(futureTask); thread.start();
Runable和Callable的区别(常见面试题,可能会问)
run()和start()的区别(常见面试题,可能会问)
线程包括哪些状态,状态之间是如何变化的?
新建T1、T2、T3三个线程,如何保证它们按顺序执行?
Java中wait和sleep方法的不同?(常见面试题,可能会问)
如何停止一个正在运行的线程?
并发安全(基本没被问过)
synchronized关键字的底层原理
Monitor实现的锁属于重量级锁,你了解过锁升级吗?
这里只给出结论,详细的可以去B站看视频
谈一谈JMM(Java内存模型)
知道CAS吗?
谈谈对volatile的理解
什么是AQS?
ReentrantLock的实现原理
synchronized和Lock有什么区别?(问过)
死锁产生的条件是什么?
聊一下ConcurrentHashMap?(问过)
导致并发程序出现问题的根本原因是什么(Java程序中怎么保证多线程的执行安全)
Java并发编程的三大特性
原子性
- 一个线程在CPU中操作不可暂停,也不可中断,要么执行完成,要么不执行
可见性
- 让一个线程对共享变量的修改对另一个线程可见
有序性
- 程序在执行的时候,程序的代码执行顺序和语句的顺序是一致的
线程池(基本都被问过)
说一下线程池的核心参数,线程池的执行原理知道吗
线程池中有哪些常见的阻塞队列
如何确定核心线程数
线程池的种类有哪些
线程池的拒绝策略
-
AbortPolicy
- 第一种拒绝策略是
AbortPolicy
,这种拒绝策略在拒绝任务时,会直接抛出一个类型为 RejectedExecutionException的RuntimeException,让你感知到任务被拒绝了,于是你便可以根据业务逻辑选择重试或者放弃提交等策略。
- 第一种拒绝策略是
-
DiscardPolicy
- 第2种拒绝策略是
DiscardPolicy
,这种拒绝策略正如它的名字所描述的一样,当新任务被提交后直接被丢弃掉,也不会给你任何的通知,相对而言存在一定的风险,因为我们提交的时候根本不知道这个任务会被丢弃,可能造成数据丢失。
- 第2种拒绝策略是
-
DiscardOldestPolicy
- 第3种拒绝策略是
DiscardOldestPolicy
,如果线程池没被关闭且没有能力执行,则会丢弃任务队列中的头结点,通常是存活时间最长的任务,这种策略与第二种不同之处在于它丢弃的不是最新提交的,而是队列中存活时间最长的,这样就可以腾出空间给新提交的任务,但同理它也存在一定的数据丢失风险。
- 第3种拒绝策略是
-
CallerRunsPolicy
- 第4种拒绝策略是 CallerRunsPolicy,相对而言它就比较完善了,当有新任务提交后,如果线程池没被关闭且没有能力执行,则把这个任务交于提交任务的线程执行,也就是谁提交任务,谁就负责执行任务。这样做主要有两点好处。
- 第一点新提交的任务不会被丢弃,这样也就不会造成业务损失。
- 第二点好处是,由于谁提交任务谁就要负责执行任务,这样提交任务的线程就得负责执行任务,而执行任务又是比较耗时的,在这段期间,提交任务的线程被占用,也就不会再提交新的任务,减缓了任务提交的速度,相当于是一个负反馈。在此期间,线程池中的线程也可以充分利用这段时间来执行掉一部分任务,腾出一定的空间,相当于是给了线程池一定的缓冲期。
为什么不建议用Executors创建线程池?
使用场景(没被问过,此处仅给出例子,具体可看B站)
项目中哪里使用了多线程
ES数据批量导入
数据汇总
异步调用
如何控制某个方法允许并发访问线程的数量
其他
谈谈对ThreadLocal的理解
ThreadLocal--内存泄露问题
JVM(没被问过,但建议掌握并在面试中找机会提及)
JVM组成
什么是程序计数器
能详细介绍Java堆吗?
什么是虚拟机栈
能不能解释下方法区?
听过直接内存吗?
类加载器
什么是类加载器,类加载器有哪些
什么是双亲委派模型?
说一下类装载的执行过程?
垃圾回收
对象什么时候可以被垃圾回收器回收?
JVM垃圾回收算法有哪些?
说一下JVM中的分代回收
说一下JVM有哪些垃圾回收器?
强引用、软引用、弱引用、虚引用的区别?
JVM实践
JVM调优的参数在哪里设置参数值
JVM调优参数有哪些?
说一下JVM调优的工具?
Java内存泄露的排查思路?
CPU飚高排查方案与思路?
未完待续,有空继续