【線程安全】線程安全的注意事項
有哪些場景需要額外注意線程安全
訪問共享變量或資源
當在多線程的環境下,多個線程去訪問共享的緩存或者對象時,同時操作會對結果造成不用程度的改變,尤其是在操作上不具備原子性的操作上時會發生這種情況,例如我們之前在【線程安全】 三類線程安全問題章節說到的i + +問題,他其實就屬于一種對共享變量訪問時,由于i + +不是原子性操作,導致結果不是預期的結果。
依賴時序的操作
在回到i + +的問題上,i + +在cup執行的時候,其實也是一種線程安全的問題。

我們再看到這張圖,兩個線程都同時去執行i + +操作,由于i + +不是原子性操作,這樣就會造成第一次i + +還沒完成就被第二個線程拿去i + +這樣就會操作兩次執行的結果一致,但結果就不對了。其實正確的時序性的操作應該是線程1的i + +操作完成之后再去執行i + +的操作。
說到底,如何保證時序的操作,你就保證他這個操作的原子性就可以了,只要讓他再執行這段代碼的邏輯時,不會被其他的線程搶去執行。
不同數據之間存在綁定關系
這種常發生在業務代碼中,比如有兩個線程,一個線程獲取學生的名字,一個線程獲取線程的學號。如果學生的學號變了,學生的名字也會跟著變化,那么這個時候就會容易出現其中一個線程更新不及時,導致學生的名字和學號對不上。導致數據有誤。那在這種情況下,我們也是需要去保持線程的原子性。
沒有申明自己是線程安全的方法
這種其實在很多新手開發會發生的錯誤,在使用多線程時,如果使用ArrayList,多個線程同時對它進行數據操作,那么這個時候就會出現數據錯誤,這個原因就是因為它不是線程安全的,如果需要在多線程的環境下去操作List集合,就需要去使用CopyOnWriteArrayList。
所以在我們日常寫多線程的時候,如果用到公共的方法,尤其是在會對數據進行操作上的方法,請使用線程安全的。
多線程帶來的性能問題
上下文切換
在計算機中,cup的核數其實是遠小于線程數的,所以cup先調度多線程的時候會不斷的去切換線程,以達到多線程看似在同時執行的場景。但是cup在調度線程的時候,會進行上下文的切換,比如線程1切換到線程2,這時就需要讀取線程2的緩存到cpu中,此時的邏輯也會可能跟著線程執行的內容不容而切換執行的邏輯。而這個資源的切換其實全靠cup來完成。這樣就會多出一些性能上的消耗。
緩存失效
在每個線程切換完上下文之后,線程的緩存可能會緩存到CPU的高速緩存中,如果當前線程邏輯還沒有執行完成就切換到另外一個線程,那么之前線程的緩存就有可能失效了,那么下次CUP再次調度到這個線程的時候,就又需要重新讀取緩存去執行。那么這個時候也會消耗不少的性能資源。
協作開銷
協作開銷通常出現在我們為了線程安全上而做的一些操作,比如對一個共享資源進行訪問,CUP可能為了保證共享資源的準確性,會禁止CPU的指令重排序。還有可能為了出于同步的目的,反復的把線程工作內存的數據刷新到主存中,然后從主存中刷新到其他線程的工作內存中,這種問題如果發生在單線程上將不會有這種狀態,但是處于多線程需要協作,同時又要避免線程安全的問題,就不得不采用上面的方法來犧牲性能。這樣就間接的降低了線程的性能。

浙公網安備 33010602011771號