首頁 >Java >java教程 >多線程並發的一些問題分享

多線程並發的一些問題分享

零下一度
零下一度原創
2017-06-28 09:13:081524瀏覽

 一概述

1.volatile

保證共享資料一旦被修改就會立即同步到共享記憶體(堆或方法區)中。

2.執行緒存取堆疊中資料的過程

# 執行緒在堆疊中建立一個變數的副本,修改完畢後將資料同步到堆中。

3.指令重排

為了提高執行效率,CPU會將沒有依賴關係的指令重新排序。如果希望控制權重新排序,可以使用volatile修飾一個變量,包含該變數的指令前後的指令各自獨立排序,前後指令不能交叉排序。

二常見問題及應對

1.原子性問題

所謂原子性,指的是一個操作不可中斷,即在多執行緒並發的環境下,一個操作一旦開始,就會在同一個CPU時間片內執行完畢。如果同一個執行緒的多個操作在不同的CPU時間片上執行,由於中間出現停滯,後面的操作在執行時可能某個共享資料被其他執行緒修改,而該修改並未同步到目前執行緒中,導致目前執行緒操作的資料與實際不符,這種由於執行不連貫導致的資料不一致問題被稱為原子性問題。

2.可見性問題

可見性問題的出現​​與執行緒存取共享資料的方式有關。當執行緒存取堆疊(方法區)中的變數時,先在堆疊中建立一個變數的副本,修改後再同步到堆中。如果一個執行緒剛建立副本,這時另一執行緒修改了變量,尚未同步到堆中,這時就會出現兩個執行緒操作同一變數的同一種狀態的現象,例如i=9 ,變數i的初始值為9,每一個執行緒的操作都是減#1。兩個線程A與B同時存取變量,B先執行i-1,在將結果i=8同步到堆中前,A線程也執行i-1,這時i=9的狀態就被執行 兩次,出現線程安全性問題。
    執行緒安全問題產生的原因:一個執行緒對共享資料的修改不能立即為其他執行緒所見。

volatile提供了一個解決方案:
    一旦一個執行緒修改了被volatile修飾的共享數據,這種修改就會立即同步到堆中,這樣其他資料從堆中存取共享資料時始終獲得的是在多個執行緒中的最新值。
    volatile的缺陷:

volatile只能保證一個執行緒從堆中取得資料時取得的是目前所有執行緒中的最新值,假如一個執行緒已經從堆複製了數據,在操作完成前,其他執行緒修改了數據,修改後的資料並不會同步到目前執行緒。

  3.有序性問題

# 為了提高執行效率,CPU會對那些沒有依賴關係的指令重新排序,重新排序後的執行結果與順序執行結果相同。
    例如,在原始碼中:
    int i=0;
    int y=1;
    CPU在執行時可能先執行“int y=1;”,接著執行“int i=0;”,執行結果與順序執行結果相同。
    指令重排在單執行緒環境下是安全的,在多執行緒環境下就可能出現問題。例如:
    執行緒A:

s=new String("sssss");//指令1flag=false;//指令2

執行緒B:

##
while(flag){
doSome();
}
s.toUpperCase();//指令3

如果執行緒A順序執行,即執行指令1,再執行指令2,執行緒B的執行不會出現問題。指令重排後,假如執行緒A先執行指令2,
    這是flag=true,切換到執行緒2,終止循環,執行指令3,由於s物件尚未建立就會出現空指標異常。
    有序性問題產生的原因:

一個執行緒對其他執行緒對共享資料的修改操作有順序要求,例如執行緒B要求執行緒A先執行指令1,再執行指令2,由於指令重排,實際上並未依照要求的順序執行,這時就出現了執行緒安全性問題。

    解決想法:

  1. #利用同步機制,讓在同一時間只有一個執行緒可以存取共享數據,效率低。

  2. 使用volatile,一個指令包含volatile修飾的變量,那麼這條指令的執行順序不變,該指令前後的指令可以各自獨立 重排,無法交叉重排。

參考:

#

以上是多線程並發的一些問題分享的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn