java線程不安全,java線程
線程不安全之線程在訪問資源時候會導致衝突。
例如下列的例子
package com.test.thread;public class TestConfilict {/** * @param args */public static void main(String[] args) {Counter counter=new Counter();for(int i=0;i<10000;i++){(new TestThread(counter)).start();}}}class TestThread extends Thread{private Counter counter;public TestThread(Counter counter){this.counter=counter;}public void run(){counter.add(1);System.out.println(counter.get());}}class Counter{protected int count=0;public void add(int value){count=count+value;count=count+value;count=count+value;count=count+value;count=count+value;count=count+value;count=count+value;count=count+value;count=count+value;count=count+value;}public int get(){return count;}}
這個例子原意就是從0增加到10000,那麼如果是單線程的話,輸出結果就是100000。我們採用多線程的方法來做,每個線程負責增加10,一共是10000個線程。
我們看結果可以發現
我們可以看到,輸出結果都不相同,而且,都不是100000,這說明了線程不安全,也就是對同一個資源進行操作的時候,產生了衝突,具體來說,就是在增加的時候,因為沒有資源保護,導致多個線程同時進入了增加的代碼。
那麼,我們可以採用加鎖的方式來解決問題。
package com.test.thread;public class TestConfilict {/** * @param args */public static void main(String[] args) {Counter counter=new Counter();for(int i=0;i<10000;i++){(new TestThread(counter)).start();}}}class TestThread extends Thread{private Counter counter;public TestThread(Counter counter){this.counter=counter;}public void run(){counter.add(1);System.out.println(counter.get());}}class Counter{protected int count=0;public synchronized void add(int value){count=count+value;count=count+value;count=count+value;count=count+value;count=count+value;count=count+value;count=count+value;count=count+value;count=count+value;count=count+value;}public int get(){return count;}}
我們可以看到,輸出結果
而且每次都是相同的,說明用加鎖的方式,使得安全執行緒了。
java裡,數組,安全執行緒的與不安全的,是什
比如說,兩個線程操作同一個ArrayList變數,那麼一個線程這一時刻讀的資料可能在下一刻要改變。
一般在類似於下面的情景下考慮安全執行緒的問題:
ArrayList products=new ArrayList ();
products用來存放生產出來的產品。
現在假設:有3個消費者線程,2個生產者線程。
每個生產者線程生產出一個產品,執行
products.add(new Product());
每個消費者線程消費一個產品執行
if(products.size()>=1){ products.remove(0);}
如果products裡現在只有一個產品可以消費,但是有2個消費者線程請求消費,那麼就有可能出現一個產品被同時消費的問題,而這是和實際不符的。
但是不同的線程訪問Vector的時候不會發生這種錯誤,因為java會有相應的機制是同一時刻只有一個線程對這個變數操作。
這就是所謂的:
Vector:是安全執行緒的
ArrayList:不是安全執行緒的
簡要說明java中安全執行緒是為何?
如果你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。如果每次運行結果和單線程啟動並執行結果是一樣的,而且其他的變數的值也和預期的是一樣的,就是安全執行緒的。
或者說:一個類或者程式所提供的介面對於線程來說是原子操作或者多個線程之間的切換不會導致該介面的執行結果存在二義性,也就是說我們不用考慮同步的問題。
安全執行緒問題都是由全域變數及靜態變數引起的。
若每個線程中對全域變數、靜態變數只有讀操作,而無寫操作,一般來說,這個全域變數是安全執行緒的;若有多個線程同時執行寫操作,一般都需要考慮線程同步,否則就可能影響安全執行緒。
比如一個 ArrayList 類,在添加一個元素的時候,它可能會有兩步來完成:1. 在 Items[Size] 的位置存放此元素;2. 增大 Size 的值。
在單線程啟動並執行情況下,如果 Size = 0,添加一個元素後,此元素在位置 0,而且 Size=1;
而如果是在多線程情況下,比如有兩個線程,線程 A 先將元素存放在位置 0。但是此時 CPU 調度線程A暫停,線程 B 得到啟動並執行機會。線程B也向此 ArrayList 添加元素,因為此時 Size 仍然等於 0 (注意哦,我們假設的是添加一個元素是要兩個步驟哦,而線程A僅僅完成了步驟1),所以線程B也將元素存放在位置0。然後線程A和線程B都繼續運行,都增加 Size 的值。
那好,現在我們來看看 ArrayList 的情況,元素實際上只有一個,存放在位置 0,而 Size 卻等於 2。這就是“線程不安全”了。
編輯本段執行緒安全性 類要成為安全執行緒的,首先必須在單線程環境中有正確的行為。如果一個類實現正確(這是說它符合規格說明的另一種方式),那麼沒有一種對這個類的對象的操作序列(讀或者寫公用欄位以及調用公用方法)可以讓對象處於無效狀態,觀察到對象處於無效狀態、或者違反類的任何不可變數、前置條件或者後置條件的情況。
此外,一個類要成為安全執行緒的,在被多個線程訪問時,不管運行時環境執行這些線程有什麼樣的時序安排或者交錯,它必須仍然有如上所述的正確行為,並且在調用的代碼中沒有任何額外的同步。其效果就是,在所有線程看來,對於安全執行緒對象的操作是以固定的、全域一致的順序發生的。
正確性與執行緒安全性之間的關係非常類似於在描述 ACID(原子性、一致性、獨立性和持久性)事務時使用的一致性與獨立性之間的關係:從特定線程的角度看,由不同線程所執行的對象操作是先後(雖然順序不定)而不是並存執行的。
執行緒安全性不是一個非真即假的命題。 Vector 的方法都是同步的,並且 Vector 明確地設計為在多線程環境中工作。但是它的執行緒安全性是有限制的,即在某些方法之間有狀態依賴(類似地,如果在迭代過程中 Vector 被其他線程修改,那麼由 Vector.iterator() 返回的 iterator會拋出ConcurrentModifiicationException)。
對於 Java 類中常見的執行緒安全性層級,沒有一種分類系統可被廣泛接受,不過重要的是在編寫類時盡量記錄下它們的安全執行緒行為。
Bloch 給出了描述五類執行緒安全性的分類方法:不可變、安全執行緒、有條件安全執行緒、線程相容和線程對立。只要明確地記錄下安全執行緒特性,那麼您是否使用這種系統都沒關係。這種系統有其局限性 -- 各類之間的界線不是百分之百地明確,而且有些情況它沒照顧到 -- 但是這套系統是一個很好的起點。這種分類系統的核心是調用者是否可以或者必須用外部同步包圍操作(......餘下全文>>