造成類在多執行緒時不安全的原因

2021-09-27 08:22:34 字數 2761 閱讀 4642

同乙個程式執行在多個執行緒中本身不會有執行緒安全問題,問題在於多個執行緒訪問共享資源時存在,如:類成員變數(普通或靜態變數),系統共享資源(檔案,資料庫)等。

同時只有多個執行緒同時對這些資源進行了寫的操作時才會發生執行緒安全問題,不對資源的進行修改時就不會存在問題。

// 執行緒不安全的計數器

public class counter

}複製**

1. 從記憶體中獲取this.count的值放到暫存器中

2. 將暫存器值加value

3. 將暫存器中的結果值寫回記憶體

複製**

但多個執行緒執行時,會共享同乙個例項的count成員變數,被排程執行時,可能會按照下列順序執行:

a: 讀取記憶體中this.count的值0到暫存器,被掛起

b: 讀取記憶體中this.count的值0到暫存器

b: 將暫存器值加value=2

b: 將暫存器結果寫回記憶體,此時記憶體中this.count值為2,執行結束

a: 再此排程a繼續執行,將暫存器值加value=3

a: 將暫存器中結果寫回記憶體,覆蓋原來的結果值為3,執行結束

複製**

兩個執行緒分別對count加2和3,兩個執行緒執行結束後,應該為5,實際為3。在兩個執行緒交叉執行時,讀到的初始值都為0,分別寫回2或3,後者覆蓋前者,如果不對這樣的多執行緒訪問進行同步控制,就會造成這種執行緒不安全的結果。

當多個執行緒訪問同乙個資源時,對先後順序敏感,就存在競態條件。導致競態條件發生的**區稱為臨界區。

上例中add()方法是乙個臨界區,它會產生競態條件。在臨界區中使用適當的同步就可以避免競態條件。

當多個執行緒訪問共享資源變數時,並且進行了寫操作,會引發競態條件。同時讀不會產生競態條件。

public void somemethod()

複製**

物件引用存在每個執行緒的執行緒棧中,但new出來的物件例項在共享堆中,如果在某個方法中建立的區域性物件不逃逸出該方法,則該類就是執行緒安全的。哪怕將該物件作為引數傳遞給其他方法,只要其他執行緒獲取不到,就還是執行緒安全的。

逃逸:即該物件不會被其他方法獲得,也不會被非區域性變數引用。

public void somemethod()

public void method2(localobject localobject)

複製**

樣例中localobject物件沒有被方法返回,也沒有被傳遞給somemethod()方法外的物件。每個執行somemethod()的執行緒都會建立自己的localobject物件,並賦值給localobject引用。因此,這裡的localobject是執行緒安全的。事實上,整個somemethod()都是執行緒安全的。即使將localobject作為引數傳給同乙個類的其它方法或其它類的方法時,它仍然是執行緒安全的。當然,如果localobject通過某些方法被傳給了別的執行緒,那它就不再是執行緒安全的了。

物件成員儲存在堆上。如果兩個執行緒同時更新同乙個物件的同乙個成員,那這個**就不是執行緒安全的。

public class notthreadsafe  

}複製**

如果兩個執行緒同時呼叫同乙個notthreadsafe例項上的add()方法,就會有競態條件問題。

注意兩個myrunnable共享了同乙個notthreadsafe物件。

因此,當它們呼叫add()方法時會造成競態條件。

notthreadsafe sharedinstance = new notthreadsafe();

new thread(new myrunnable(sharedinstance)).start();

new thread(new myrunnable(sharedinstance)).start();

public class myrunnable implements runnable

public void run()

}複製**

當然,如果這兩個執行緒在不同的notthreadsafe例項上呼叫call()方法,

就不會導致競態條件。下面是稍微修改後的例子:

new thread(new myrunnable(new notthreadsafe())).start();

new thread(new myrunnable(new notthreadsafe())).start();

複製**

現在兩個執行緒都有自己單獨的notthreadsafe物件,呼叫add()方法時就會互不干擾,再也不會有競態條件問題了。所以非執行緒安全的物件仍可以通過某種方式來消除競態條件。

執行緒安全的類:不包含競態條件,即多執行緒時不存在共享資源變數或存在共享資源變數時進行了適當的同步控制

請注意add()方法以加法操作的結果作為乙個新的immutablevalue類例項返回

而不是直接對它自己的value變數進行操作。

public class immutablevalue

public int getvalue()

public immutablevalue add(int valuetoadd)

}複製**

請注意immutablevalue類的成員變數value是通過建構函式賦值的,並且在類中沒有set方法。這意味著一旦immutablevalue例項被建立,value變數就不能再被修改,這就是不可變性。但你可以通過getvalue()方法讀取這個變數的值。

HashMap 執行緒不安全的原因

hashmap執行緒安全的問題,在各大面試中都會被問到,屬於常考熱點題目。雖然大部分讀者都了解它不是執行緒安全的,但是再深入一些,問它為什麼不是執行緒安全的,仔細說說原理,用圖畫出一種非執行緒安全的情況?1.8之後又做了什麼改善了這點?很多讀者可能一時想不出很好的答案。我們關注下面的 void tr...

解決TestNG多執行緒併發時,執行緒不安全問題

testng的強大功能之一就是支援多執行緒併發測試,但前提是需要我們的程式是執行緒安全的,否則實際測試過程中就會出錯。例如當我們初始化兩個driver時,如果不保證執行緒安全,則兩個driver在執行時會相互干擾甚至死掉。未進行執行緒安全優化之前的 用selenium 3,firefox瀏覽器驅動需...

多執行緒之執行緒不安全演示

什麼樣的 會導致執行緒不安全.有時後,子類繼承重寫父類方法後會導致執行緒不安全.package com.ldp.demo01 import lombok.extern.slf4j.slf4j import j a.util.arraylist import j a.util.list author ...