日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

tcp out of order解决_Java解决CAS机制中ABA问题的方案

發布時間:2025/3/12 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 tcp out of order解决_Java解决CAS机制中ABA问题的方案 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

通過對atomic包的分析我們知道了CAS機制,我們在看一下CAS的公式。

CAS(V,A,B)1:V表示內存中的地址2:A表示預期值3:B表示要修改的新值

CAS的原理就是預期值A與內存中的值相比較,如果相同則將內存中的值改變成新值B。這樣比較有兩類:

第一類:如果操作的是基本變量,則比較的是值是否相等。

第二類:如果操作的是對象的引用,則比較的是對象在內存的地址是否相等。

總結一句話就是:比較并交換。

其實CAS是Java樂觀鎖的一種實現機制,在Java并發包中,大部分類就是通過CAS機制實現的線程安全,它不會阻塞線程,如果更改失敗則可以自旋重試,但是它也存在很多問題:

1:ABA問題,也就是說從A變成B,然后就變成A,但是并不能說明其他線程并沒改變過它,利用CAS就發現不了這種改變。2:由于CAS失敗后會繼續重試,導致一致占用著CPU。

用一個圖來說明ABA的問題。

線程1準備利用CAS修改變量值A,但是在修改之前,其他線程已經將A變成了B,然后又變成A,即A->B->A,線程1執行CAS的時候發現仍然為A,所以CAS會操作成功,但是其實目前這個A已經是其他線程修改的了,但是線程1并不知道,最終內存值變成了B,這就導致了ABA問題。

接下來我們看一個關于ABA的例子:

public class AtomicMarkableReferenceTest { private final static String A = "A"; private final static String B = "B"; private final static AtomicReference ar = new AtomicReference<>(A); public static void main(String[] args) { new Thread(() -> { try { Thread.sleep(Math.abs((int) (Math.random() * 100))); } catch (InterruptedException e) { e.printStackTrace(); } if (ar.compareAndSet(A, B)) { System.out.println("我是線程1,我成功將A改成了B"); } }).start(); new Thread(() -> { if (ar.compareAndSet(A, B)) { System.out.println("我是線程2,我成功將A改成了B"); } }).start(); new Thread(() -> { if (ar.compareAndSet(B,A)) { System.out.println("我是線程3,我成功將B改成了A"); } }).start(); }}

上面例子運行結果如下,線程1并不知道線程2和線程3已經改過了值,線程1發現此時還是A則會更改成功,這就是ABA:

所以每種技術都有它的兩面性,在解決了一些問題的同時也出現了一些新的問題,在JDK中也為我們提供了兩種解決ABA問題的方案,接下來我們就看一下是怎樣解決的。

本篇文章的主要內容:

1:AtomicMarkableReference實例和源碼解析2:AtomicStampedReference實例和源碼解析

一、AtomicMarkableReference實例和源碼解析

上面的例子如果利用這個類去實現,會怎樣呢?稍微改變上面的代碼如下:

public class AtomicMarkableReferenceTest { private final static String A = "A"; private final static String B = "B"; private final static AtomicMarkableReference ar = new AtomicMarkableReference<>(A, false); public static void main(String[] args) { new Thread(() -> { try { Thread.sleep(Math.abs((int) (Math.random() * 100))); } catch (InterruptedException e) { e.printStackTrace(); } if (ar.compareAndSet(A, B, false, true)) { System.out.println("我是線程1,我成功將A改成了B"); } }).start(); new Thread(() -> { if (ar.compareAndSet(A, B, false, true)) { System.out.println("我是線程2,我成功將A改成了B"); } }).start(); new Thread(() -> { if (ar.compareAndSet(B, A, ar.isMarked(), true)) { System.out.println("我是線程3,我成功將B改成了A"); } }).start(); }}

運行結果如下:

是不是解決了這個ABA的問題,AtomicMarkableReference僅僅用一個boolean標記解決了這個問題,那接下來我們進入源碼看看它是怎么一種機制。

1:成員變量

private volatile Pair pair;

定義了一個被關鍵字volatile修飾的Pair,那Pair是什么對象呢?

private static class Pair {//封裝了我們傳遞的對象 final T reference;//這個就是boolean標記 final boolean mark; private Pair(T reference, boolean mark) { this.reference = reference; this.mark = mark; } static Pair of(T reference, boolean mark) { return new Pair(reference, mark); }}

2:構造函數

public AtomicMarkableReference(V initialRef, boolean initialMark) { pair = Pair.of(initialRef, initialMark);}

這個構造函數就是調用Pair中的of()方法,把我們需要操作的對象和boolean標記傳遞進去。

那說明以后的操作都是基于Pair這個類進行操作了。那接下來我們看一下它的CAS方法是怎樣定義的。

//expectedReference表示我們傳遞的預期值//newReference表示將要更改的新值//expectedMark表示傳遞的預期boolean類型標記//newMark表示將要更改的boolean類型標記的新值。public boolean compareAndSet(V expectedReference, V newReference, boolean expectedMark, boolean newMark) { Pair current = pair; return expectedReference == current.reference && expectedMark == current.mark && ((newReference == current.reference && newMark == current.mark) || casPair(current, Pair.of(newReference, newMark)));}

上面的return后的代碼分解后主要有三大邏輯:

第一個邏輯&&(第二個邏輯 || 第三個邏輯)

第一個邏輯:預期對象和預期的boolean類型標記必須和內部的Pair中相等

expectedReference == current.reference && expectedMark == current.mark

如果第一個邏輯是true,才能繼續往下判斷,否則直接返回false。

第二個邏輯:如果這個邏輯為true,就不在執行第三個邏輯了

newReference == current.reference && newMark == current.mark

如果新的將要更改的對象和新的將要更改的boolean類型的標記和內部Pair的相等,則就不在執行第三個邏輯了。如果為false,則繼續往下執行第三個邏輯

第三個邏輯:CAS邏輯

casPair(current, Pair.of(newReference, newMark))

如果預期的對象和預期的boolean標記和Pair都相等,但是新的對象和新的boolean標記和Pair不相等,此時需要進行CAS更新了

從上面的講解大家能不能總結出來它是怎樣解決ABA的問題的,現在我們總結以下:

它是通過把操作的對象和一個boolean類型的標記封裝成Pair,而Pair有被volatile修飾,說明只要更改其他線程立刻可見,而只有Pair中的兩個成員變量都相等。來解決CAS中ABA的問題的。一個偽流程圖如下:

二、AtomicStampedReference實例和源碼解析

上面我們知道了AtomicMarkableReference是通過添加一個boolean類型標記和操作的對象封裝成Pair來解決ABA問題的,但是如果想知道被操作對象更改了幾次,這個類就無法處理了,因為它僅僅用一個boolean去標記,所以AtomicStampedReference就是解決這個問題的,它通過一個int類型標記來代替boolean類型的標記。

上面的例子更改如下:

public class AtomicMarkableReferenceTest { private final static String A = "A"; private final static String B = "B"; private static AtomicInteger ai = new AtomicInteger(1); private final static AtomicStampedReference ar = new AtomicStampedReference<>(A, 1); public static void main(String[] args) { new Thread(() -> { try { Thread.sleep(Math.abs((int) (Math.random() * 100))); } catch (InterruptedException e) { e.printStackTrace(); } if (ar.compareAndSet(A, B, 1,2)) { System.out.println("我是線程1,我成功將A改成了B"); } }).start(); new Thread(() -> { if (ar.compareAndSet(A, B, ai.get(),ai.incrementAndGet())) { System.out.println("我是線程2,我成功將A改成了B"); } }).start(); new Thread(() -> { if (ar.compareAndSet(B, A, ai.get(),ai.incrementAndGet())) { System.out.println("我是線程3,我成功將B改成了A"); } }).start(); }}

運行結果:

1:成員變量

private volatile Pair pair;private static class Pair { final T reference; final int stamp; private Pair(T reference, int stamp) { this.reference = reference; this.stamp = stamp; } static Pair of(T reference, int stamp) { return new Pair(reference, stamp); }}

這種結果和AtomicMarkableReference中的Pair結構類似,只不過是把boolean類型標記改成了int類型標記。

2:構造函數

public AtomicStampedReference(V initialRef, int initialStamp) { pair = Pair.of(initialRef, initialStamp);}

3:CAS方法

public boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp) { Pair current = pair; return expectedReference == current.reference && expectedStamp == current.stamp && ((newReference == current.reference && newStamp == current.stamp) || casPair(current, Pair.of(newReference, newStamp)));}

上面分析了JDK中解決CAS中ABA問題的兩種解決方案,他們的原理是相同的,就是添加一個標記來記錄更改,兩者的區別如下:

1:AtomicMarkableReference利用一個boolean類型的標記來記錄,只能記錄它改變過,不能記錄改變的次數2:AtomicStampedReference利用一個int類型的標記來記錄,它能夠記錄改變的次數。

atomic包中的類已經介紹結束,接下來一篇文章我將對atomic做一個總結,然后就開始Java并發包中lock包進行全面解析。

總結

以上是生活随笔為你收集整理的tcp out of order解决_Java解决CAS机制中ABA问题的方案的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。