@@ -559,80 +559,31 @@ private void increment(int x){
559559如果你把上面这段代码贴到 IDE 中运行,会发现并不能得到目标输出结果。有朋友已经在 Github 上指出了这个问题:[ issue #2650 ] ( https://github.com/Snailclimb/JavaGuide/issues/2650 ) 。下面是修正后的代码:
560560
561561``` java
562- private volatile int a = 0 ; // 共享变量,初始值为 0
563- private static final Unsafe unsafe;
564- private static final long fieldOffset;
565-
566- static {
567- try {
568- // 获取 Unsafe 实例
569- Field theUnsafe = Unsafe . class. getDeclaredField(" theUnsafe" );
570- theUnsafe. setAccessible(true );
571- unsafe = (Unsafe ) theUnsafe. get(null );
572- // 获取 a 字段的内存偏移量
573- fieldOffset = unsafe. objectFieldOffset(CasTest . class. getDeclaredField(" a" ));
574- } catch (Exception e) {
575- throw new RuntimeException (" Failed to initialize Unsafe or field offset" , e);
576- }
577- }
578-
579- public static void main(String [] args) {
580- CasTest casTest = new CasTest ();
581-
582- Thread t1 = new Thread (() - > {
583- for (int i = 1 ; i <= 4 ; i++ ) {
584- casTest. incrementAndPrint(i);
585- }
586- });
587-
588- Thread t2 = new Thread (() - > {
589- for (int i = 5 ; i <= 9 ; i++ ) {
590- casTest. incrementAndPrint(i);
591- }
592- });
593-
594- t1. start();
595- t2. start();
596-
597- // 等待线程结束,以便观察完整输出 (可选,用于演示)
598- try {
599- t1. join();
600- t2. join();
601- } catch (InterruptedException e) {
602- Thread . currentThread(). interrupt();
603- }
604- }
605-
606562// 将递增和打印操作封装在一个原子性更强的方法内
607563private void incrementAndPrint(int targetValue) {
608564 while (true ) {
609565 int currentValue = a; // 读取当前 a 的值
610- // 只有当 a 的当前值等于目标值的前一个值时,才尝试更新
611- if (currentValue == targetValue - 1 ) {
612- if (unsafe. compareAndSwapInt(this , fieldOffset, currentValue, targetValue)) {
613- // CAS 成功,说明成功将 a 更新为 targetValue
614- System . out. print(targetValue + " " );
615- break ; // 成功更新并打印后退出循环
616- }
617- // 如果 CAS 失败,意味着在读取 currentValue 和执行 CAS 之间,a 的值被其他线程修改了,
618- // 此时 currentValue 已经不是 a 的最新值,需要重新读取并重试。
566+ // 如果当前值已经达到或超过目标值,说明已被其他线程处理,跳过
567+ if (currentValue >= targetValue) {
568+ return ;
619569 }
620- // 如果 currentValue != targetValue - 1,说明还没轮到当前线程更新,
621- // 或者已经被其他线程更新超过了,让出CPU给其他线程机会。
622- // 对于严格顺序递增的场景,如果 current > targetValue - 1,可能意味着逻辑错误或死循环,
623- // 但在此示例中,我们期望线程能按顺序执行。
624- Thread . yield(); // 提示CPU调度器可以切换线程,减少无效自旋
570+ // 尝试 CAS 操作:如果当前值等于 targetValue - 1,则原子地设置为 targetValue
571+ if (unsafe. compareAndSwapInt(this , fieldOffset, currentValue, targetValue)) {
572+ // CAS 成功后立即打印,确保打印的就是本次设置的值
573+ System . out. print(targetValue + " " );
574+ return ;
575+ }
576+ // CAS 失败,重新读取并重试
625577 }
626578}
627579```
628-
629580在上述例子中,我们创建了两个线程,它们都尝试修改共享变量 a。每个线程在调用 ` incrementAndPrint(targetValue) ` 方法时:
630581
6315821 . 会先读取 a 的当前值 ` currentValue ` 。
6325832 . 检查 ` currentValue ` 是否等于 ` targetValue - 1 ` (即期望的前一个值)。
6335843 . 如果条件满足,则调用` unsafe.compareAndSwapInt() ` 尝试将 ` a ` 从 ` currentValue ` 更新到 ` targetValue ` 。
6345854 . 如果 CAS 操作成功(返回 true),则打印 ` targetValue ` 并退出循环。
635- 5 . 如果 CAS 操作失败,或者 ` currentValue ` 不满足条件,则当前线程会继续循环(自旋),并通过 ` Thread.yield() ` 尝试让出 CPU,直到成功更新并打印或者条件满足 。
586+ 5 . 如果 CAS 操作失败,说明有其他线程同时竞争,此时会重新读取 ` currentValue ` 并重试,直到成功为止 。
636587
637588这种机制确保了每个数字(从 1 到 9)只会被成功设置并打印一次,并且是按顺序进行的。
638589
0 commit comments