Java併發原理學習筆記+總結+實戰(2)——線程安全性問題

1、線程帶來的風險

    java對線程的支持其實是一把雙刃劍。雖然java提供了相應的語言和庫,以及一種明確的跨平臺內存模型,這些工具簡化了併發應用程序的開發,但同時也提高的對開發人員的技術要求,因爲更多的程序中會使用線程。

1.1 線程的安全性問題

    線程安全性可能是非常複雜的,在沒有充分同步的情況下,多個線程中的操作次序是不可預測的,甚至會產生奇怪的結果。

/**
 * UnsafeSequence:線程不安全例子.
 *
 * @author YUSIR
 * @version 2019-03-01
 */
public class UnsafeSequence {
  private int value;

  /**
   * @return 返回一個唯一的數值.
   */
  public int getNext() {
    return value++;
  }
}

    上面的demo中將會產生一個整數值序列,該序列中的每個值都是唯一的。該類在單線程環境中能正常的工作,但是在多線程環境中則不能。

    UnSafeSequence的問題在於,如果執行時機不對,那麼兩個線程的調用getNext()時會得到相同的值。雖然遞增運算value++看上去是單個操作,安踏事實上包含了三個獨立操作:讀取value,將value+1,將計算結果寫入value。由於運行時可能將多個線程之間的操作交替執行,因此這兩個線程可能同時進行讀操作,從而使它們得到相同的值,並都將其加一,結果就是不同的線程返回了相同的值。

     圖中給出了不同線程之間的一種交替執行情況。執行時序按照從左到右的順序遞增,每行表示一個線程的動作。這些交替執行是給出的最糟糕的執行情況(事實上,由於指令重排序的可能,因此實際情況可能會更糟糕),目的是爲了說明,如果錯誤地假設程序中的操作將按照某種特定順序來執行,那麼會存在各種可能的、不可預知的危險。

1.2 活躍性問題

    安全性的含義是“永遠不發生糟糕的事情”,二活躍性則關注與另一個目標,即“某件正確的事情最終會發生”。當某個操作無法繼續執行下去時,就會發生活躍性問題。在串行程序中,活躍性問題的形式之一就是無意中造成的無限循環,從而使循環之後的代碼無法得到執行。線程將帶來其他一些活躍性問題。例如,如果線程A在等待線程B釋放其所持有的資源時,二線程B永遠都不釋放該資源,那麼A就會永久地等待下去。主要是包括:

  • 死鎖
  • 飢餓
  • 活鎖

1.3 性能問題

    性能問題包括多個方面,例如服務時間過長,響應不靈敏,吞吐率過低,資源消耗過高,或者可伸縮性較低等。使用多線程技術,在能夠提升程序性能的同時,不可避免的也會帶來某種程度上的運行時開銷。在多線程程序中,當線程調度器臨時掛起活躍線程並轉而運行另一個線程時,就會頻繁地出現上下文切換操作,這種操作將帶來極大的開銷:保存和恢復執行上下文,丟失局部性,並且CPU時間將跟過地花在線程調度而不是線程運行上。當線程共享數據時,必須使用同步機制,而這些機制往往會抑制某些編譯器的優化,是內存緩存區中的數據無效,以及增加共享內存總線的同步流量。

 

 

 

--

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章