面试题答案
一键面试原理
- volatile:保证变量在多线程之间的可见性,当一个线程修改了volatile变量的值,新值对其他线程立即可见。它不会引起线程上下文切换和调度,不保证原子性。通过内存屏障实现,禁止指令重排序。
- synchronized:通过对象的内置锁机制实现线程同步,一个线程进入同步块或方法时,会获取对象的锁,其他线程只能等待锁的释放。它保证原子性、可见性和有序性,会导致线程上下文切换和调度。
功能
- volatile:主要解决变量在多线程间的可见性问题,适用于一写多读场景,当一个线程修改变量,其他线程能及时看到变化。
- synchronized:不仅保证可见性,还能保证原子性和有序性。可用于控制对共享资源的访问,避免并发访问导致的数据不一致问题。
性能
- volatile:因为不涉及线程上下文切换和调度,性能开销相对较小,适用于读多写少的场景。
- synchronized:由于线程竞争锁会导致上下文切换和调度,性能开销较大,尤其在高并发且锁竞争激烈的场景下。
适用场景
- 优先选择volatile的场景:
- 状态标记量,如
boolean running = false
,当一个线程修改running
为true
,其他线程需要立即感知到。
public class VolatileExample { private volatile boolean running = false; public void start() { running = true; } public void doWork() { while (!running) { // 执行其他操作 } // running变为true后执行的操作 } }
- 读多写少的共享变量,如配置参数等,多个线程频繁读取,偶尔有一个线程修改。
- 状态标记量,如
- 必须使用synchronized的场景:
- 对共享资源进行复杂操作,如读写操作都可能改变共享资源状态的场景,像银行转账操作,需要保证原子性。
public class BankAccount { private int balance; public BankAccount(int initialBalance) { this.balance = initialBalance; } public synchronized void transfer(BankAccount to, int amount) { if (this.balance >= amount) { this.balance -= amount; to.balance += amount; } } }
- 保证操作的原子性、可见性和有序性,比如在多线程环境下对计数器进行自增操作。
public class Counter { private int count = 0; public synchronized void increment() { count++; } public synchronized int getCount() { return count; } }