標籤:不可變對象 nal 類型 自己的 產生 nbsp 調優 程式員 原子操作
使用多線程就可能會存線上程安全的問題。很多 java 程式員對寫多線程都很掙紮,或者僅僅理解那些是安全執行緒的代碼,那些不是。這篇文章我並不是詳述安全執行緒,詳述同步機制的文章,相反我只是用一個簡單的非安全執行緒的代碼例子引領大家,然後重點是去理解什麼是安全執行緒,以及怎樣使代碼變得安全執行緒。
好了,下面我們先來看一個非安全執行緒的代碼,可以嘗試找找為什麼非安全執行緒?
/** 非安全執行緒類*/ public class Counter {private int count; public int getCount(){ return count++; }}
大家應該能看得出來,上面代碼是非安全執行緒的,因為 ++ 操作不是原子操作,讀取、更新、寫回的時候可能會出問題。
比如現在有多個線程同時訪問 getCount() 方法,那麼這些線程可能同時操作 count ,部分結果出現重疊。例如,Thread 1 正在更新 count,Thread 2 讀取 count,但是仍然是擷取到舊的值,那麼最後Thread 2 就會覆蓋掉 Thead 1 的更新,所以並發環境下我們要考慮 ++ 操作的原子性。
在 Java 編程中,有許多方式去編寫安全執行緒的代碼:
1)、使用 synchronized 關鍵字,synchonized 可以 lock getCount() 方法,某個時刻只有一個線程可以執行它,也就實現了安全執行緒
2)、使用原子的 Integer,原子的 Integer 可以保證 ++ 操作的原子性
好了,我們了看看安全執行緒版本的 Counter
public class Counter { private int count; AtomicInteger atomicCount = new AtomicInteger( 0 ); /**1、synchronized 所以安全執行緒*/ public synchronized int getCount(){ return count++; } /* * 2、原子增長的操作,所以安全執行緒 */ public int getCountAtomically(){ return atomicCount.incrementAndGet(); }} java 安全執行緒比較重要的點: 在 java 編程中,記住這些關鍵點可以幫你避免一些嚴重的並發問題,比如條件競爭或死結。1)、不可變對象預設是安全執行緒的,因為他們一旦被建立就不會被修改。比如 String 是不可變對象,是安全執行緒的。 2)、唯讀、final 類型的變數也是安全執行緒的 3)、鎖也是一種安全執行緒的方式 4) 、static 變數,如果沒有被恰當的使用同步,也會引發安全執行緒問題 5)、使用安全執行緒的類: Vector, Hashtable, ConcurrentHashMap, String etc. 6)、原子操作是安全執行緒的 reading a 32 bit int from memory because its an atomic operation it can‘t interleave with other thread. 7) 、本地變數也是安全執行緒的,因為每個線程都有自己的變數 copy.使用本地變數是一種保證代碼安全執行緒的好方法。(ThreadLocal) 8)、 多線程之間的共用對象儘可能的少,也就儘可能的避免安全執行緒的問題 9) 、Volatile 關鍵字 好了,以上就是所有的關於編寫安全執行緒類或代碼和避免並發問題的要點。老實說,安全執行緒是有點難掌握的概念,你需要去考慮並發,進而看代碼是否安全執行緒。當然 jvm 也可以重排序代碼,實現自己調優。但是相同的代碼在開發環境正常不一定能保證線上上也正常。因為 jvm 會去自我調優,重排序等操作去最佳化代碼,這些也可能會產生安全執行緒的問題。
怎樣去寫安全執行緒的代碼(Java)