1、通過引入版本戳(stamp)機制解決ABA問題:
每次修改時遞增版本號 執行CAS時同時檢查值和版本號 即使值相同但版本不同,操作也會失敗
2、具體代碼實現
import java.util.concurrent.atomic.AtomicStampedReference; public class AtomicStampedReferenceDemo { // 賬戶余額初始值為100,版本號初始為0private static final AtomicStampedReference< Integer> accountBalance = new AtomicStampedReference<> ( 100 , 0 ) ; public static void main( String[ ] args) throws InterruptedException { System.out.println( "============== AtomicStampedReference解決ABA問題 ==============" ) ; System.out.println( "初始賬戶余額: " + accountBalance.getReference( ) +"元, 版本號: " + accountBalance.getStamp( )) ; System.out.println( ) ; // 創建ABA操作線程Thread abaThread = new Thread(( ) - > {int[] stampHolder = new int[1 ]; int currentValue; / / 第一次修改:100 → 50 System.out.println( stampHolder[0 ]) ; currentValue = accountBalance.get( stampHolder) ; int currentStamp = stampHolder[0 ]; System.out.println( "[ABA線程] 讀取余額: " + currentValue + "元, 版本: " + currentStamp) ; boolean success = accountBalance.compareAndSet( currentValue, 50 , currentStamp, currentStamp + 1 ) ; System.out.println( "[ABA線程] 修改余額為50 元: " + ( success ? "成功" : "失敗") + ", 新版本: " + accountBalance.getStamp( )) ; System.out.println( "當前余額: " + accountBalance.getReference( ) + "元" ) ; System.out.println( ) ; // 第二次修改:50 → 100 currentValue = accountBalance.get( stampHolder) ; currentStamp = stampHolder[ 0 ] ; System.out.println( "[ABA線程] 讀取余額: " + currentValue + "元, 版本: " + currentStamp) ; success = accountBalance.compareAndSet( currentValue,100 ,currentStamp,currentStamp + 1 ) ; System.out.println( "[ABA線程] 恢復余額為100元: " + ( success ? "成功" : "失敗" ) +", 新版本: " + accountBalance.getStamp( )) ; System.out.println( "當前余額: " + accountBalance.getReference( ) + "元" ) ; System.out.println( ) ; } ) ; // 創建轉賬線程Thread transferThread = new Thread(( ) - > {int[] stampHolder = new int[1 ]; int currentValue = accountBalance.get( stampHolder) ; int currentStamp = stampHolder[0 ]; System.out.println( "[轉賬線程] 讀取余額: " + currentValue + "元, 版本: " + currentStamp) ; System.out.println( "[轉賬線程] 開始處理轉賬...") ; try {/ / 模擬處理耗時Thread.sleep( 2000 ) ; } catch ( InterruptedException e) {e.printStackTrace( ) ; }System.out.println( "[轉賬線程] 轉賬處理完成,嘗試更新賬戶") ; / / 嘗試更新余額(增加50 元)boolean success = accountBalance.compareAndSet( currentValue, currentValue + 50 , currentStamp, currentStamp + 1 ) ; System.out.println( "\n== == = 轉賬操作結果 == == = ") ; System.out.println( "操作結果: " + ( success ? "成功" : "失敗")) ; System.out.println( "預期版本: " + currentStamp + ", 實際版本: " + accountBalance.getStamp( )) ; System.out.println( "預期余額: " + currentValue + "元, 當前余額: " + accountBalance.getReference( ) + "元" ) ; System.out.println( "=====================\n " ) ; } ) ; // 啟動線程transferThread.start( ) ; Thread.sleep( 500 ) ; // 確保轉賬線程先讀取初始值abaThread.start( ) ; // 等待線程完成abaThread.join( ) ; transferThread.join( ) ; System.out.println( "\n 最終賬戶余額: " + accountBalance.getReference( ) + "元, 版本號: " + accountBalance.getStamp( )) ; System.out.println( "============== 演示結束 ==============" ) ; }
}
== == == == == == == AtomicStampedReference解決ABA問題 == == == == == == ==
初始賬戶余額: 100 元, 版本號: 0 [ 轉賬線程] 讀取余額: 100 元, 版本: 0
[ 轉賬線程] 開始處理轉賬.. .
0
[ ABA線程] 讀取余額: 100 元, 版本: 0
[ ABA線程] 修改余額為50元: 成功, 新版本: 1
當前余額: 50 元[ ABA線程] 讀取余額: 50 元, 版本: 1
[ ABA線程] 恢復余額為100元: 成功, 新版本: 2
當前余額: 100 元[ 轉賬線程] 轉賬處理完成,嘗試更新賬戶== == = 轉賬操作結果 == == =
操作結果: 失敗
預期版本: 0 , 實際版本: 2
預期余額: 100 元, 當前余額: 100 元
== == == == == == == == == == = 最終賬戶余額: 100 元, 版本號: 2
== == == == == == == 演示結束 == == == == == == == Process finished with exit code 0