搜尋

首頁  >  問答  >  主體

Java SE 多執行緒安全性問題產生的原因?

可能像圖片上的程式碼出現負數的機率不大,但在if語句後面加上Thread.sleep(10);就能看到輸出負數

阿神阿神2727 天前980

全部回覆(5)我來回復

  • 大家讲道理

    大家讲道理2017-06-12 09:28:22

    不知道你要問什麼,多個執行緒同時讀取一個資源出現不同步問題很正常,因為可能一個執行緒取得值的時候另一個執行緒剛好在寫值,這就會產生同步問題。

    解決辦法有很多,最笨的直接程式碼區塊上加同步,整個鎖起來;好點的是用線程安全的類,比如AtomInteger這種,保證同步;如果對多線程很有研究,甚至可以只加很少的鎖就能完成任務。

    回覆
    0
  • 怪我咯

    怪我咯2017-06-12 09:28:22

    執行緒的呼叫順序是不保證有序的,其根本原因在於JVM協調資源時執行緒之間的切換。

    回覆
    0
  • 習慣沉默

    習慣沉默2017-06-12 09:28:22

    本質原因是CPU為了提高效率會對指令進行重新排序

    回覆
    0
  • 我想大声告诉你

    我想大声告诉你2017-06-12 09:28:22

    沒有對num進行同步,不能保證當前執行緒對num的值改之後,其他執行緒可以立刻看到,題主可以了解下Java記憶體模型。
    以題主的程式碼為例,假設執行到最後num=1,三個執行緒同時執行到if判斷,都能判斷出通過,那就有可能出現負數。

    回覆
    0
  • 黄舟

    黄舟2017-06-12 09:28:22

    1、記憶體可見性
    2、修改的原子性

    由於num是類別靜態變量,那麼它會被存到堆中,在run()方法執行時拷貝一份副本到棧中存儲,當有多個線程修改時,可能同時拿到一樣的副本,但是由於執行的前後順序,一個執行緒修改並寫入了該變量,雖然堆中num已經發生變化,但是其他執行緒並不知道,它們會繼續修改那份副本。然後修改後寫入堆中,那麼這樣就會覆蓋先前執行緒的修改,進而導致狀態的不一致問題。
    那麼如果才能確保線程安全性呢。那就要確保修改num之前保證對堆區修改的可見性,修改之前再拿一份副本(即使之前已經拿過了),這個可用volatile關鍵字來保證。

    原子性,由於num--實際執行是兩個操作,那麼就會存在執行順序問題。即使在前面說過要用volatilel來確保可見性。但是還會有修改被其他線程覆蓋的情形,只不過幾率變小了。怎麼保證原子性呢,可以採用synchronized關鍵字,Lock機制,以及JDK並發工具包等。對於這種情形,最簡單的方法就是

    private static AtomicInteger num=new AtomicInteger(100);

    回覆
    0
  • 取消回覆