java面试题(代码1)

7 阅读1分钟

分析下这段代码,有哪些问题和优化

class Counter {
    private volatile int counter = 0;

    public void incr() {
        counter++;
    }

    public int get() {
        return counter;
    }
}

这段代码存在明显的并发安全问题,主要源于对volatile关键字的误用和原子性缺失:

⚠️ ‌核心问题分析

  1. 原子性缺失
    counter++ 操作实际包含三个步骤:读取值 → 加1 → 写回值。‌**volatile仅保证可见性,无法保证复合操作的原子性**‌。当多线程并发执行时:

    • 线程A读取 counter=0
    • 线程B读取 counter=0
    • 双方各自加1后写回,最终结果变为 1 而非预期的 2(更新丢失)
  2. volatile的局限性
    volatile确保变量修改对所有线程立即可见,但‌无法解决多线程同时修改的竞争条件‌。对于自增操作,仍需额外同步机制。

  3. 长整型扩展问题
    若未来需支持更大范围(如long),volatile long在32位系统上可能因非原子写导致数据撕裂(高位/低位更新分离)。


🛠️ ‌优化方案

✅ 方案1:使用原子类(推荐)

javaCopy Code
import java.util.concurrent.atomic.AtomicInteger;

class Counter {
    private final AtomicInteger counter = new AtomicInteger(0);
    
    public void incr() {
        counter.incrementAndGet(); // 原子自增
    }
    
    public int get() {
        return counter.get(); // 可见性由原子类内部保证
    }
}

优势‌:

  • 通过CAS(Compare-And-Swap)实现无锁并发,高性能
  • 避免线程阻塞,适合高并发场景

✅ 方案2:同步锁控制

javaCopy Code
class Counter {
    private int counter = 0;
    
    public synchronized void incr() {
        counter++; // 同步块内操作保证原子性
    }
    
    public int get() {
        return counter; // 读操作无需同步(仅当incr同步时)
    }
}

适用场景‌:

  • 低频修改高频读取时效率较高
  • 需注意锁粒度对性能的影响