MST
星途 面试题库

面试题:自定义实现类似Java Atomic类的原子操作机制

假设不存在Java现有的Atomic系列类,要求你基于Java的基础特性,如CAS(Compare - and - Swap)机制,自定义实现一个能够对长整型数据进行原子操作的类,需要完整给出类的代码实现,并详细解释每一步的设计思路和可能存在的问题。
46.6万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

代码实现

import sun.misc.Unsafe;

import java.lang.reflect.Field;

public class MyAtomicLong {
    private static final Unsafe unsafe;
    private static final long valueOffset;
    private volatile long value;

    static {
        try {
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            unsafe = (Unsafe) theUnsafe.get(null);
            valueOffset = unsafe.objectFieldOffset
                    (MyAtomicLong.class.getDeclaredField("value"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }

    public MyAtomicLong(long initialValue) {
        value = initialValue;
    }

    public long get() {
        return value;
    }

    public void set(long newValue) {
        value = newValue;
    }

    public long incrementAndGet() {
        for (; ; ) {
            long current = get();
            long next = current + 1;
            if (compareAndSet(current, next)) {
                return next;
            }
        }
    }

    public long getAndIncrement() {
        for (; ; ) {
            long current = get();
            long next = current + 1;
            if (compareAndSet(current, next)) {
                return current;
            }
        }
    }

    public boolean compareAndSet(long expect, long update) {
        return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
    }
}

设计思路解释

  1. 获取Unsafe实例:通过反射获取Unsafe类中的theUnsafe字段,从而得到Unsafe的实例。Unsafe类提供了底层的CAS操作等功能。这一步通过static代码块实现,保证在类加载时就获取Unsafe实例。
  2. 获取字段偏移量:使用unsafe.objectFieldOffset方法获取value字段在内存中的偏移量valueOffset。这个偏移量用于后续的CAS操作,告诉Unsafe在哪个内存位置进行操作。
  3. 定义成员变量:定义一个volatile修饰的long型变量value,用于存储实际的值。volatile关键字保证了内存可见性,即一个线程对value的修改,其他线程能够立即看到。
  4. 构造函数MyAtomicLong(long initialValue)构造函数用于初始化value的值。
  5. get方法get()方法直接返回value的值,因为valuevolatile的,所以能保证获取到最新的值。
  6. set方法set(long newValue)方法直接设置value的值。
  7. incrementAndGet方法:通过无限循环,先获取当前值current,然后计算下一个值next = current + 1,接着使用compareAndSet方法尝试将valuecurrent更新为next。如果更新成功,说明没有其他线程同时修改value,则返回next;如果更新失败,说明有其他线程同时修改了value,则继续循环重试。
  8. getAndIncrement方法:逻辑与incrementAndGet类似,不同的是先返回当前值current,再尝试更新valuenext
  9. compareAndSet方法:调用unsafe.compareAndSwapLong方法进行CAS操作,尝试将对象this中偏移量为valueOffset处的值从expect更新为update,如果更新成功返回true,否则返回false

可能存在的问题

  1. 反射获取Unsafe实例:通过反射获取Unsafe实例是一种非标准的做法,因为Unsafe类在设计上是不希望被普通应用程序直接使用的。在不同的JVM版本中,Unsafe类的实现和theUnsafe字段的访问方式可能会有所变化,这可能导致代码在不同环境下的兼容性问题。
  2. 性能问题:在高并发场景下,compareAndSet操作可能会频繁失败,导致大量的循环重试,这会消耗大量的CPU资源。可以考虑使用更复杂的算法,如乐观锁机制结合分段锁等方式来提高性能。
  3. ABA问题:标准的CAS操作存在ABA问题,即一个值从A变为B,再变回A,CAS操作会认为这个值没有发生变化。虽然在这个简单的自增场景下ABA问题影响不大,但如果是更复杂的业务场景,可能需要额外的处理,比如使用带有版本号的原子操作。