MST

星途 面试题库

面试题:多线程环境下Java可见性的问题及解决

假设有多个线程同时读写共享变量,可能会出现可见性问题。请详细阐述这种情况下Java内存模型的工作机制,以及如何使用Java并发包中的工具(如Atomic类、锁机制)来解决可见性问题,并对比它们在保证可见性方面的优缺点。
14.2万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

Java内存模型工作机制

  1. 主内存与工作内存:Java内存模型将内存分为主内存和每个线程的工作内存。主内存是所有线程共享的,存储了共享变量。每个线程都有自己的工作内存,工作内存保存了该线程使用到的共享变量的副本。
  2. 线程操作:线程对共享变量的读取和写入操作都在自己的工作内存中进行。当线程要读取共享变量时,会先从主内存将变量值复制到自己的工作内存;当线程要写入共享变量时,先在工作内存中修改,然后再将修改后的值刷新回主内存。
  3. 可见性问题产生:由于不同线程的工作内存相互独立,当一个线程修改了共享变量并刷新回主内存后,其他线程可能并不能立即看到这个修改,因为它们工作内存中的变量副本还是旧值,这就导致了可见性问题。

使用Java并发包工具解决可见性问题

  1. Atomic类
    • 原理:Atomic类利用了硬件级别的原子操作(如CAS - Compare and Swap)来保证对共享变量的操作是原子性的,并且通过内存屏障来保证可见性。当一个线程修改了Atomic变量的值,其他线程能够立即看到这个变化。
    • 优点:无锁操作,性能较高,适用于高并发环境下对单个变量的原子性和可见性操作。例如在计数器场景中,使用AtomicInteger比使用synchronized关键字实现的计数器性能更好。
    • 缺点:只能对单个变量进行操作,对于多个变量的原子性和可见性操作支持有限。如果需要对多个变量进行原子操作,需要额外的处理方式。
  2. 锁机制(如synchronized、ReentrantLock)
    • 原理:以synchronized为例,当一个线程进入同步块时,会先从主内存中读取共享变量的值到工作内存,退出同步块时,会将工作内存中的变量值刷新回主内存。这就保证了在同步块内对共享变量的操作对其他线程是可见的。ReentrantLock类似,通过加锁和解锁操作保证可见性。
    • 优点:可以保证一段代码块内的操作具有原子性和可见性,适用于对多个共享变量进行操作的场景。例如在银行转账操作中,涉及到两个账户余额的修改,可以使用锁机制来保证整个转账操作的原子性和可见性。
    • 缺点:加锁和解锁操作会带来额外的性能开销,在高并发环境下,可能会因为竞争锁而导致性能下降。而且如果使用不当,容易出现死锁问题。