Java 執行緒安全
1. 什麼是執行緒安全?
可變資源(記憶體)間執行緒共享。
2. 如何實現執行緒安全?
- 不共享資源;
- 共享不可變資源;
- 共享可變資源:
- 可見性;
- 操作原子性;
- 禁止重排序;
1. 不共享資源
1. 可重入函式:函式體內部不涉及任何外部變數;
// 可重入函式 public static int addTwo(int num) { return num + 3; }
2. 使用ThreadLoacl:
final static ThreadLocal<String> mThreadLocal = new ThreadLocal<>();public static void test() { Runnable runnable = new Runnable() { @Override public void run() { mThreadLocal.set(UUID.randomUUID().toString()); System.out.println(Thread.currentThread().getName() + " -- token : " + mThreadLocal.get()); } };for (int idx = 0; idx < 3; idx++) { Thread thread = new Thread(runnable); thread.start(); } }
上面的結果就是每個執行緒都有自己的副本,輸出的值也不相同。
ThreadLocal作用及實現原理:
ThreadLocal作用是用於執行緒間資料隔離,ThreadLocal是執行緒內部資料儲存類,通過ThreadLocal將資料儲存在指定執行緒中,儲存後只能通過指定執行緒獲取資料,其它執行緒是獲取不到資料的。ThreadLocal是一個Map(ThreadLocalMap),Key是執行緒物件Thread.currentThread,值為儲存的資料,在Hander的Looper類中的ThreadLocal儲存的是Looper物件。
在Looper中ThreadLocal是靜態常量儲存於靜態儲存區,執行緒中工作記憶體中使用的是ThreadLocal的主記憶體的副本,但是由於ThreadLocal是一個Map物件,Key又是執行緒(Thread.currentThread),只能通過指定執行緒獲取ThreadLocal中的資料,所以,做到了執行緒間資料隔離互不影響。
/** * Returns the value in the current thread's copy of this * thread-local variable. If the variable has no value for the * current thread, it is first initialized to the value returned * by an invocation of the {@link #initialValue} method. * * @return the current thread's value of this thread-local */ public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); } /** * Sets the current thread's copy of this thread-local variable * to the specified value. Most subclasses will have no need to * override this method, relying solely on the {@link #initialValue} * method to set the values of thread-locals. * * @param value the value to be stored in the current thread's copy of * this thread-local. */ public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
通過ThreadLocal原始碼中get()和set()方法,操作的變數是ThreadLocalMap。
ThreadLocalMap的建立:
/** * Get the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @return the map */ ThreadLocalMap getMap(Thread t) { return t.threadLocals; } /** * Create the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @param firstValue value for the initial entry of the map */ void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
ThreadLocalMap就是一個Map,而向Map中儲存資料的key是Thread的this(map.set(this, value);),就是將值儲存到執行緒上的,就是通過Map將thread的this和value繫結。
ThreadLocal使用建議:
-
- 宣告為全域性靜態final變數;
- 避免儲存大量物件;
- 使用完後及時移除物件;
2. 共享不可變資源
使用final關鍵字
3. 共享可變資源
1. 禁止重排序特性;
2. 可見性:
使用final和volatile關鍵字:
final:1. 修飾變數不可被修改;2. 修飾類不可被繼承;3. 修飾方法不可被覆寫;4. 禁止重排序特性;
volatile:1. 可見性 -- 多執行緒操作時,其中一個執行緒修改了volatile修飾的變數,會通知其它執行緒變數改變;2. 禁止重排序;
PS:每個執行緒都有自己的工作記憶體,工作記憶體將主記憶體變數copy到工作記憶體後,再執行操作。所以,導致了多執行緒時變數資料不一致性。
2.加鎖:鎖釋放後強制將快取資料重新整理到主記憶體。
3. 操作原子性:加鎖。