日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java并发教程–原子性和竞争条件

發布時間:2023/12/3 java 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java并发教程–原子性和竞争条件 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

原子性是多線程程序中的關鍵概念之一。 我們說一組動作是原子的,如果它們都以不可分割的方式作為一個單一的操作執行。 認為多線程程序中的一組操作將被串行執行是理所當然的,可能會導致錯誤的結果。 原因是由于線程干擾,這意味著如果兩個線程對同一數據執行多個步驟,則它們可能會重疊。

以下交織示例顯示了兩個線程執行多個操作(循環中的打印)以及它們如何重疊:


public class Interleaving {public void show() {for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName() + " - Number: " + i);}}public static void main(String[] args) {final Interleaving main = new Interleaving();Runnable runner = new Runnable() {@Overridepublic void run() {main.show();}};new Thread(runner, "Thread 1").start();new Thread(runner, "Thread 2").start();} }

執行時,將產生不可預測的結果。 舉個例子:

Thread 2 - Number: 0 Thread 2 - Number: 1 Thread 2 - Number: 2 Thread 1 - Number: 0 Thread 1 - Number: 1 Thread 1 - Number: 2 Thread 1 - Number: 3 Thread 1 - Number: 4 Thread 2 - Number: 3 Thread 2 - Number: 4

在這種情況下,不會發生任何錯誤,因為它們只是打印數字。 但是,當您需要在不同步的情況下共享對象的狀態(其數據)時,這會導致競爭條件的存在。

比賽條件

如果由于線程交織而有可能產生不正確的結果,則您的代碼將處于競爭狀態。 本節描述了兩種競爭條件:

  • 先檢查后行動
  • 讀-修改-寫
  • 為了消除競爭條件并增強線程安全性,我們必須通過使用同步使這些操作成為原子操作。 以下各節中的示例將顯示這些競爭條件的影響。

    先行動后競賽狀態

    當您有一個共享字段并希望依次執行以下步驟時,會出現此競爭條件:

  • 從字段中獲取值。
  • 根據上一次檢查的結果來做一些事情。
  • 這里的問題是,當第一個線程在上次檢查后要執行操作時,另一個線程可能已經插入并更改了字段的值。 現在,第一個線程將基于不再有效的值執行操作。 通過示例更容易看到這一點。

    UnsafeCheckThenAct應該一次更改字段號 。 在對changeNumber方法的調用之后,應導致執行else條件:

    public class UnsafeCheckThenAct {private int number;public void changeNumber() {if (number == 0) {System.out.println(Thread.currentThread().getName() + " | Changed");number = -1;}else {System.out.println(Thread.currentThread().getName() + " | Not changed");}}public static void main(String[] args) {final UnsafeCheckThenAct checkAct = new UnsafeCheckThenAct();for (int i = 0; i < 50; i++) {new Thread(new Runnable() {@Overridepublic void run() {checkAct.changeNumber();}}, "T" + i).start();}} }

    但是由于此代碼未同步,因此(無法保證)可能會導致對該字段進行多次修改:

    T13 | Changed T17 | Changed T35 | Not changed T10 | Changed T48 | Not changed T14 | Changed T60 | Not changed T6 | Changed T5 | Changed T63 | Not changed T18 | Not changed

    這種競爭條件的另一個示例是延遲初始化 。

    解決此問題的一種簡單方法是使用同步。

    SafeCheckThenAct是線程安全的,因為它已通過同步對共享字段的所有訪問來消除競爭條件。

    public class SafeCheckThenAct {private int number;public synchronized void changeNumber() {if (number == 0) {System.out.println(Thread.currentThread().getName() + " | Changed");number = -1;}else {System.out.println(Thread.currentThread().getName() + " | Not changed");}}public static void main(String[] args) {final SafeCheckThenAct checkAct = new SafeCheckThenAct();for (int i = 0; i < 50; i++) {new Thread(new Runnable() {@Overridepublic void run() {checkAct.changeNumber();}}, "T" + i).start();}} }

    現在,執行此代碼將始終產生相同的預期結果。 只有一個線程會更改該字段:

    T0 | Changed T54 | Not changed T53 | Not changed T62 | Not changed T52 | Not changed T51 | Not changed ...

    在某些情況下,還有其他機制會比同步整個方法更好,但我不會在本文中討論它們。

    讀-修改-寫競爭條件

    在這里,當執行以下一組操作時,會出現另一種競爭條件:

  • 從字段中獲取值。
  • 修改值。
  • 將新值存儲到該字段。
  • 在這種情況下,還有另一種危險的可能性,那就是丟失了對該字段的某些更新。 一種可能的結果是:

    Field’s value is 1. Thread 1 gets the value from the field (1). Thread 1 modifies the value (5). Thread 2 reads the value from the field (1). Thread 2 modifies the value (7). Thread 1 stores the value to the field (5). Thread 2 stores the value to the field (7).

    如您所見,值5的更新已丟失。

    讓我們看一個代碼示例。 UnsafeReadModifyWrite共享一個數字字段,每次都會遞增:

    public class UnsafeReadModifyWrite {private int number;public void incrementNumber() {number++;}public int getNumber() {return this.number;}public static void main(String[] args) throws InterruptedException {final UnsafeReadModifyWrite rmw = new UnsafeReadModifyWrite();for (int i = 0; i < 1_000; i++) {new Thread(new Runnable() {@Overridepublic void run() {rmw.incrementNumber();}}, "T" + i).start();}Thread.sleep(6000);System.out.println("Final number (should be 1_000): " + rmw.getNumber());} }

    您能發現引起比賽狀況的復合動作嗎?

    我敢肯定你做到了,但是為了完整起見,我還是會解釋一下。 問題出在增量( number ++ )中。 這似乎是一個動作,但實際上,它是三個動作的序列(get-increment-write)。

    執行此代碼時,我們可能會看到丟失了一些更新:

    2014-08-08 09:59:18,859|UnsafeReadModifyWrite|Final number (should be 10_000): 9996

    由于無法保證線程如何交織,因此取決于您的計算機,將很難重現此更新丟失。 如果無法重現上面的示例,請嘗試UnsafeReadModifyWriteWithLatch ,它使用CountDownLatch來同步線程的開始,并重復測試一百次。 您可能應該在所有結果中看到一些無效值:

    Final number (should be 1_000): 1000 Final number (should be 1_000): 1000 Final number (should be 1_000): 1000 Final number (should be 1_000): 997 Final number (should be 1_000): 999 Final number (should be 1_000): 1000 Final number (should be 1_000): 1000 Final number (should be 1_000): 1000 Final number (should be 1_000): 1000 Final number (should be 1_000): 1000 Final number (should be 1_000): 1000

    這個例子可以通過使所有三個動作原子化來解決。

    SafeReadModifyWriteSynchronized在對共享字段的所有訪問中使用同步:

    public class SafeReadModifyWriteSynchronized {private int number;public synchronized void incrementNumber() {number++;}public synchronized int getNumber() {return this.number;}public static void main(String[] args) throws InterruptedException {final SafeReadModifyWriteSynchronized rmw = new SafeReadModifyWriteSynchronized();for (int i = 0; i < 1_000; i++) {new Thread(new Runnable() {@Overridepublic void run() {rmw.incrementNumber();}}, "T" + i).start();}Thread.sleep(4000);System.out.println("Final number (should be 1_000): " + rmw.getNumber());} }

    讓我們看另一個刪除此競爭條件的示例。 在這種特定情況下,由于字段號與其他變量無關,因此我們可以使用原子變量。

    SafeReadModifyWriteAtomic使用原子變量來存儲字段的值:

    public class SafeReadModifyWriteAtomic {private final AtomicInteger number = new AtomicInteger();public void incrementNumber() {number.getAndIncrement();}public int getNumber() {return this.number.get();}public static void main(String[] args) throws InterruptedException {final SafeReadModifyWriteAtomic rmw = new SafeReadModifyWriteAtomic();for (int i = 0; i < 1_000; i++) {new Thread(new Runnable() {@Overridepublic void run() {rmw.incrementNumber();}}, "T" + i).start();}Thread.sleep(4000);System.out.println("Final number (should be 1_000): " + rmw.getNumber());} }

    以下帖子將進一步說明機制,如鎖定或原子變量。

    結論

    這篇文章解釋了在非同步多線程程序中執行復合操作時隱含的一些風險。 為了強制執行原子性并防止線程交織,必須使用某種類型的同步。

    • 您可以在github上查看源代碼。

    翻譯自: https://www.javacodegeeks.com/2014/08/java-concurrency-tutorial-atomicity-and-race-conditions.html

    總結

    以上是生活随笔為你收集整理的Java并发教程–原子性和竞争条件的全部內容,希望文章能夠幫你解決所遇到的問題。

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

    主站蜘蛛池模板: 丁香六月色婷婷 | 东京av男人的天堂 | 91av看片 | 毛片在线免费 | 欧美巨鞭大战丰满少妇 | 久久羞羞 | 开心黄色网| 我和岳m愉情xxxⅹ视频 | 中国av一区二区三区 | 惊艳大片mv视频 | 可以看的毛片 | 七月婷婷综合 | 日韩二区视频 | 久草香蕉在线 | 99热这里只有精品66 | 国产suv精品一区二区三区 | 欧美人妖69xxxxxhd3d | 国产亚洲综合在线 | 午夜影院久久久 | 超碰日本 | 久久久久久久伊人 | 日日碰碰 | 99精品在线播放 | 综合久色 | 极品销魂美女少妇尤物 | 不卡av免费在线观看 | 姑娘第5集在线观看免费好剧 | 亚洲天堂网一区二区 | 中文字幕少妇 | 校园春色在线观看 | 亚洲人成电影一区二区在线 | 岛国av在线 | 最新精品国产 | 一级黄色视屏 | 国产影视一区二区三区 | a天堂资源 | av5566| 日韩精品视频免费 | 亚洲天堂中文字幕在线 | 老女人黄色片 | 在线视频观看免费 | 粗大的内捧猛烈进出在线视频 | 一区亚洲| 人妻少妇精品视频一区二区三区 | 老太脱裤让老头玩ⅹxxxx | 中文字幕最新在线 | 黄色天堂av | 亚洲国产精彩中文乱码av | 日韩蜜桃视频 | 黄色片地址 | 亚洲麻豆一区 | 一级做a视频 | 一二区视频| 日韩理论在线观看 | 在办公室被c到呻吟的动态图 | 五月天小说网 | 免费观看黄网站 | 成人在线观看一区二区三区 | 久久精品免费观看 | 粉嫩精品久久99综合一区 | 久久久久香蕉视频 | 在线视频 一区二区 | 少妇真人直播免费视频 | 亚洲AV永久无码国产精品国产 | 亚洲精品一区二区三区四区 | 精品国产av 无码一区二区三区 | 乱色专区| 色哟哟免费 | 欧美性大战久久久久久久蜜桃 | 最新毛片网 | 亚洲国产第一 | 国产乱人乱精一区二视频国产精品 | 亚洲一区二区电影网 | 校园春色av | 午夜免费看片 | 日本天天色 | 欧美激情一区在线 | 欧美日韩少妇精品 | 69xxx中国| 黑人和白人做爰 | 91精品国产一区二区三区香蕉 | 三级自拍| 制服下的诱惑暮生 | 风韵少妇性饥渴推油按摩视频 | 怡红院一区二区三区 | 五月天中文字幕 | 国产精品久久久久久久久借妻 | 成人av动漫| 99久久久久久久久久 | 国产毛片一区 | 操操网 | 视频区小说区 | 日韩一区二区三区视频 | 欧美青青草 | 校花被c到呻吟求饶 | 九九九色 | 亚欧美一区二区三区 | 色伊人影院 | 污免费在线观看 |