Skip to content

修复Unsafe CAS的代码仍存在的问题#2855

Open
paigeman wants to merge 1 commit into
Snailclimb:mainfrom
paigeman:main
Open

修复Unsafe CAS的代码仍存在的问题#2855
paigeman wants to merge 1 commit into
Snailclimb:mainfrom
paigeman:main

Conversation

@paigeman
Copy link
Copy Markdown
Contributor

相关issue

#2650

相关pull request

#2801

问题

按照当前版本的文章,代码应该是:

import sun.misc.Unsafe;
import java.lang.reflect.Field;

public class CasTest {
    // 1. 共享变量,必须用 volatile 保证可见性
    private volatile int a = 0;

    private static Unsafe unsafe;
    private static long fieldOffset;

    // 2. 在静态块中通过反射初始化 Unsafe 并获取变量 a 的内存偏移量
    static {
        try {
            Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafeField.setAccessible(true);
            unsafe = (Unsafe) theUnsafeField.get(null);

            // 获取 CasTest 类中名为 "a" 的属性在内存中的偏移地址
            fieldOffset = unsafe.objectFieldOffset(CasTest.class.getDeclaredField("a"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        CasTest casTest = new CasTest();

        // 线程 1:负责处理 1 到 4 的数字
        new Thread(() -> {
            for (int i = 1; i < 5; i++) {
                casTest.incrementAndPrint(i);
            }
        }).start();

        // 线程 2:负责处理 5 到 9 的数字
        new Thread(() -> {
            for (int i = 5; i < 10; i++) {
                casTest.incrementAndPrint(i);
            }
        }).start();
    }

    /**
     * 将递增和打印操作完美封装在一起的原子化方法
     * @param targetValue 线程当前循环期望达到的目标值
     */
    private void incrementAndPrint(int targetValue) {
        while (true) {
            int currentValue = a; // 每次循环强行读取当前主内存中 a 的最新值

            // 拦截机制:如果当前值已经达到或超过目标值,说明别的线程或者之前已经处理过了,直接跳过
            if (currentValue >= targetValue) {
                return;
            }

            // 核心锁控:严格限制!只有当当前值正好等于【目标值 - 1】时,才允许当前线程出手
//            if (currentValue == targetValue - 1) {
                // 此时执行硬件级原子操作 CAS:
                // 如果内存里的值确实是 targetValue - 1,就把它原子性地替换为 targetValue
                if (unsafe.compareAndSwapInt(this, fieldOffset, currentValue, targetValue)) {

                    // 【关键点】CAS 成功后,立刻在当前线程打印目标值!
                    // 因为外面有 currentValue == targetValue - 1 的严格卡扣,
                    // 保证了这一瞬间绝对没有其他线程能突破进来修改它,打印出来的数字百分之百是按顺序的。
                    System.out.print(targetValue + " ");
                    return; // 大功告成,退出当前数字的自旋
                }
//            }
            // 如果上述条件不满足(比如前置数字还没被另一个线程加到位),线程就会在这里反复自旋等待
        }
    }
}

可是运行结果仍然不是顺序的,可能的结果是:

1 5 6 7 8 9

原因

按照当前版本代码里的注释:

// 尝试 CAS 操作:如果当前值等于 targetValue - 1,则原子地设置为 targetValue

可是当前版本的代码是没进行这个操作的。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant