【Java 併發編程】 07 線程安全 — 沒有共享就沒有傷害! 東西多了就不知道珍惜了! 我有殘疾,你隨意修改!我搶到了我加把鎖!即使被傷害,世界也要充滿愛!

1. 什麼是線程安全呢?

線程安全並非“線程安全”,大家不要望文生義,所謂的線程安全其實指的是內存的安全,隨着操作系統的發展,不再是單核CPU,而是多核多任務,也就意味的進程的併發執行,回想一下是不是可以一邊敲着代碼,一邊聽着歌,此時問題來了,爲什麼這兩個進程互不影響呢?操作系統對此做了一系列的保障,是的每一個進程有自己的一塊內存空間,進程之間是隔離的,彼此不能互相訪問。

每個進程有自己的一塊特殊公共的區域,稱爲堆內存,既然是公共區域,那麼一個進程裏面的多個線程都可訪問的到,這也就是 bug 的起源。一個線程訪問公共的成員變量,並對此進行了修改,於是發生線程切換,等回來繼續執行的時候發現數據被其它線程進行了修改。由此可見 線程安全是指在沒有任何限制的情況下,堆內存中的數據可以被多個線程訪問並意外修改。

2. 如何解決線程安全問題呢?

成員變量變爲局部變量(線程封閉)—— 沒有共享就沒有傷害

在這裏插入圖片描述
學習 java 基礎的時候我們知道,內存可以分爲堆內存和棧內存,凡是 new 出來的對象都是放在堆內存中的。方法的調用是壓棧進行的。👉 成員變量和局部變量的區別,成員變量是類中的變量,隨着類的消失而消失,而局部變量是在方法中(局部變量在棧中),隨着方法的調用結束而消失。 方法在棧裏面,壓棧進行的,調用方法是先進後出,方法的調用和棧的存儲結構有關,我們稱爲調用棧。

多個線程可以調用相同的方法,且每個線程可以給方法傳不同的參數,也就意味着每個線程都有自己獨立的調用棧。
在這裏插入圖片描述
可見 java 的局部變量是線程安全的,每個線程有自己的調用棧,方法中的局部變量和方法同生共死,且不被其線程共享,回到我們最初線程安全的問題,多個線程可以修改共享變量,此時我們將共享變量變爲局部變量,也就是成員變量變爲局部變量,不就可以解決線程安全問題了麼。可見沒有共享就沒有傷害。

我有殘疾,你隨意修改,對我沒有差別啦!

我們上述通過將成員變量變爲局部變量,可以解決線程安全問題,可是有的時候我們並不想讓成員變量變爲局部變量,那個怎麼辦呢,你是否還記得 final 關鍵字,final 關鍵字修飾的類我們成爲太監類,修飾的變量爲太監變量,這是一個殘疾的變量,一旦賦了初值,將不會再修改。此時也就意味着多個線程訪問該成員變量時,只能讀,不能寫,即使你寫了,也不生效。可見這個方法對線程也是安全的,但是這個方法真的狗。

東西多了就不知道珍惜了— ThreadLocal

回到最初,多個線程訪問共享變量,導致線程不安全的問題發生了,可見這個共享變量少,如果多的話,不就沒問題了麼,就像共享單車,大街上滿地都是。每一個線程訪問修改共享變量時,拷貝一份到本地,也就意味着,每一個線程都有自己的一套共享變量,是不是可以通過一個map集合來存儲 ,
線程名作爲key,線程本地的共享變量作爲 value 值。每一個線程都有自己的共享變量數據,各自修改各自的,互不影響。具體我們會拿出一篇博客講解。

我搶到了就加一把鎖 — Lock

我們可以通過鎖的方式,如果共享單車的數量還是很少,共享資源有限,當我搶到共享資源以後,給共享資源加鎖,(不要給共享單車加鎖,這裏只是講例子)等不不使用了就釋放鎖。獲取資源前先獲取鎖,操作結束後釋放鎖。

class X {
  private final Lock rtl = new ReentrantLock();
  int count;
  public void incream() {
    // 獲取鎖
    rtl.lock();  
    try {
      count+=1;
    } finally {
      // 釋放鎖
      rtl.unlock();
    }
  }
}

即使被傷害,世界也要充滿愛!—樂觀鎖

共享變量少,線程多,所以受傷害。如果線程數目少呢,受傷害的概率就變得越來越小,當單線程的時候,也就不存在傷害了。通過鎖可以解決線程安全問題,但是每次獲取鎖和釋放鎖都需要花費很大的資源,於是CAS 出現了,CAS(Compare And Swap),比較並交換,是樂觀鎖的一種實現。它適用於併發量較小的情況。樂觀鎖認爲每一次操作大概率不存在線程併發的情況,操作時無需加鎖,而是對數據進行版本比較,就像git 和svn 一樣,每次push 的時候,都需要檢查自己更新前的版本是否和目前版本是否一致,一致纔可以推送數據。

此時會後一個ABA問題,例如某一個變量的值爲A,某一個線程修改變成了B,後來又被其它線程改回了A,此時的A雖然和以前的A沒有任何差別,但是版本號已經不再是原來的版本號。只要數據被修改,版本號就加一。

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