文章目录
提问问题
开发功能⾃测⽤什么⼯具,⾃测看什么指标?
多线程是怎么⽤的?不同使用场景有什么作用?
怎么写⼀个懒加载的单例?volatile的可见性是具体怎么实现的?
Hashmap的线程问题,如何解决的?
⼤并发量的情况下,冲突⼤的情况下(上万的情况下),使⽤sycronized还是⽤CAS呢?
雪花算法⽤过吗?
Mysql怎么实现的可重复读,底层实现?
MVCC实现原理,Mvcc解决了什么问题,隐藏列包括什么内容?
消息队列等幂了解吗?
消息队列交替打印123456如何实现,有多少种实现思路?
分享对JVM的理解
问题1
在开发功能自测时,通常会使用单元测试框架,如JUnit或TestNG,来编写和执行测试用例。
自测的指标主要包括代码覆盖率、测试用例的执行结果(成功、失败、错误)、性能指标(如响应时间、吞吐量)等。
问题2
多线程在Java中的使用通常涉及到两种主要的实现方式:继承Thread类和实现Runnable接口。
继承Thread类需要覆盖其run()方法来定义线程要执行的任务,而实现Runnable接口则需要实现run()方法并将其实例传递给Thread类的构造函数来创建线程。
不同使用场景下多线程的作用如下:
-
Web服务器:在Web服务器中,多线程用于同时处理多个客户端请求,提高服务器的吞吐量和响应速度。每个线程处理一个请求,这样可以充分利用服务器的CPU资源。
-
并行计算:对于需要大量计算的任务,如图像处理、数据分析、科学计算等,可以通过多线程并行处理,以充分利用多核处理器的性能优势,加快计算速度。
-
数据爬虫:网络爬虫在下载和解析网页时,使用多线程可以并发地执行这些操作,显著提高爬取效率。
-
实时数据处理:实时监控、日志处理等需要快速响应的应用场景,多线程可以实时处理和响应事件,保证数据的及时处理。
-
游戏开发:在游戏开发中,多线程可以用于分离游戏逻辑处理和图形渲染,或者用于实现复杂的游戏世界模拟,提高游戏的性能和流畅度。
多线程编程还需要考虑线程间的同步和通信,以保证数据的一致性和完整性。这通常通过同步机制(如synchronized关键字、ReentrantLock等)、共享对象、等待/通知机制(Object.wait()、notify()等)、以及并发集合类(如ConcurrentHashMap、CopyOnWriteArrayList等)来实现。
问题3
在Java中实现懒加载的单例模式,最常用的方法是双重检查锁定(Double-Check Locking, DCL)模式。这种模式确保了线程安全,并尽可能地延迟了单例对象的初始化。以下是使用DCL模式的单例实现代码:
public class Singleton {
// 使用volatile关键字修饰实例,确保可见性和防止指令重排序
private static volatile Singleton instance;
private Singleton() {
// 私有构造方法,防止外部直接实例化
}
public static Singleton getInstance() {
// 首先检查实例是否已经存在
if (instance == null) {
// 使用synchronized关键字同步整个方法,确保只有一个线程能进入临界区
synchronized (Singleton.class) {
// 再次检查,防止其他线程在第一次检查后,单例已被创建的情况
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
在上述代码中,volatile
关键字用于保证变量instance
的可见性。当一个线程修改了instance
的值,这个新值对于其他线程是立即可见的。这是因为volatile
会阻止指令重排序,确保在构造Singleton
实例时,先执行构造函数中的所有操作,再将实例赋值给instance
变量。
volatile
关键字的可见性保证了一个线程对共享变量的修改对其他线程立即可见,它是通过禁止指令重排序和内存屏障来实现的。
问题4
HashMap在多线程环境下可能会出现线程安全问题,比如死循环和数据丢失。解决HashMap线程问题的方法包括使用Collections.synchronizedMap()包装HashMap或使用ConcurrentHashMap代替HashMap。
问题5
在大并发量且冲突大的场景下(如上万的情况下),通常会选择使用CAS(Compare-And-Swap)而不是synchronized,因为CAS在多数情况下性能更高,它通过无锁编程减少了线程竞争和上下文切换。
问题6
雪花算法是一种生成唯一ID的算法,通常用于分布式系统中。它通过服务器ID和时间戳来生成单调递增且唯一的ID。
雪花算法的核心思想是将一个64位的长整数划分为几个部分,每个部分代表不同的含义,通常包括以下几个字段:
-
时间戳(Timestamp):通常占据41位,表示自某个固定起始时间点以来的毫秒数。这个字段使得生成的ID具有时间顺序性,并且随着时间的推移单调递增。
-
数据中心标识(Datacenter Id):通常占据10位,用于区分不同的数据中心或机器集群。
-
机器标识(Worker Id):通常占据10位,用于在同一数据中心内标识不同的机器或者进程。
-
序列号(Sequence Number):通常占据12位,用于在同一毫秒内为同一机器上的请求生成唯一的ID。
由于时间戳占据了大部分位数,因此雪花算法生成的ID随着时间的推移是单调递增的。同时,通过数据中心标识和机器标识的组合,可以确保即使在分布式部署的环境下,也能生成全局唯一的ID。序列号的使用则进一步确保了在高并发情况下的唯一性。
雪花算法生成的ID形式类似于:
12345678901234567890123456789012
,其中包含了上述的各个字段信息。通过这种方式,不仅保证了ID的唯一性,还使得ID具有一定的自描述性和可读性。
问题7
MySQL通过MVCC(多版本并发控制)实现了可重复读。
以下是其底层实现的一些关键要点:
锁机制:MySQL 使用两种锁来控制并发访问:共享锁(S锁)和排他锁(X锁)。当一个事务读取一行数据时,它会给这行数据加上 S 锁,以防止其他事务修改;当事务尝试更新或删除一行数据时,会给这行数据加上 X 锁,以防止其他事务同时进行修改或读取。
MVCC:MySQL 的 InnoDB 存储引擎支持 MVCC,允许事务基于不同的数据快照进行读取,而不是直接读取当前的数据。每个事务开始时都会创建一个唯一的读取视图(Read View),在这个视图中,事务可以看到在它开始之前已经提交的所有修改,但看不到之后提交的修改。
Next-Key Locks:InnoDB 还使用 Next-Key Locks 来避免幻读。幻读是指在一个事务的生命周期内,另一个并发事务插入了新的行,导致第一个事务在多次读取时看到不同的结果集。Next-Key Locks 是索引记录加上间隙(即索引记录之间的空间)的锁,这样可以确保在读取范围内不会插入新的记录。
Undo Logs:为了支持 MVCC,InnoDB 存储引擎维护了撤销日志(undo logs)。当事务开始时,它会记录下所有的修改操作,如果事务失败或回滚,这些操作可以通过撤销日志恢复原状,从而提供了多个版本的数据。
Read Views:当事务开始时,它会创建一个 Read View。在事务执行期间,如果有新的事务提交,那么当前事务的 Read View 不会改变,这意味着当前事务可以继续看到它开始时的数据快照,直到它提交或回滚。
隔离级别与锁升级:在默认的可重复读隔离级别下,InnoDB 会在必要时将 S 锁升级为 X 锁来避免幻读。但在某些情况下,比如在只有一个索引记录的情况下,InnoDB 可能会跳过 S 锁,直接应用 X 锁。
通过上述机制,MySQL 确保了可重复读隔离级别下的事务在其生命周期内始终能够看到一致的数据快照,从而避免了脏读(Dirty Read)、不可重复读(Non-repeatable Read)和幻读(Phantom Read)。
问题8
在MVCC中,每个事务都有自己的数据版本,当读取数据时,只读取到事务开始之前的数据版本,从而避免了读取过程中的数据变动。
MVCC解决了传统锁机制下读写冲突和死锁问题,提高了并发性能。
隐藏列通常包括事务ID和回滚段等。
问题9
幂等性指的是相同的请求多次执行,系统的状态不会发生变化。在消息队列中,可以通过消息唯一标识符、业务逻辑判断等方式来保证幂等性。
问题10
在实现消息队列的交替打印123456的场景中,可以采用6种思路。
生产者-消费者模型:
创建两个生产者线程,分别向消息队列发送数字1,2,3和4,5,6。创建两个消费者线程,一个消费1,3,5,另一个消费2,4,6,并交替打印它们。原子操作:
使用原子操作(如CAS,Compare-And-Swap)来控制打印逻辑。当一个线程打印完数字后,它会通过原子操作更新一个变量来指示下一个数字应由另一个线程打印。信号量/互斥锁:
使用信号量或互斥锁来控制线程间的同步。设置一个计数器,初始值为0,两个线程轮流增加计数器并根据计数器的值打印相应的数字。条件变量:
使用条件变量来等待和通知。两个线程各自等待特定条件,打印完自己的数字后通知对方。消息队列中的自定义属性:
在消息队列中给每条消息添加一个标识,比如奇数消息或偶数消息。两个消费者线程分别消费奇数消息和偶数消息,并交替打印。分布式锁:
如果是在分布式系统中,可以使用分布式锁来保证只有一个线程可以执行打印操作。当一个线程获得锁后,它会打印数字,并在打印完成后释放锁,另一个线程再获得锁并执行。
问题11
JVM作为Java程序的运行环境,它抽象了与底层操作系统和硬件平台的差异,为Java程序提供了统一的运行环境。
JVM的组成(类加载器、运行时数据区、执行引擎等)、工作原理(类加载、编译、执行)、性能调优(垃圾回收、内存管理)等方面。
写在最后
PS:以上是网络上收集的一些常见的问题以及自己对答案搜索整理;一次整理基本上就是面试一次的题量,适合对自己的知识的查缺补漏
面试一般根据岗位要求或者简历上写的来进行扩展提问,也有些是直接问公司常用到的相关方面的技术问题,无论怎么准备都祝大家能拿到心怡的offer!