1. 程式人生 > 其它 >Java 執行緒安全

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. 可見性:

      使用finalvolatile關鍵字:

      final:1. 修飾變數不可被修改;2. 修飾類不可被繼承;3. 修飾方法不可被覆寫;4. 禁止重排序特性;

      volatile:1. 可見性 -- 多執行緒操作時,其中一個執行緒修改了volatile修飾的變數,會通知其它執行緒變數改變;2. 禁止重排序;

      PS:每個執行緒都有自己的工作記憶體,工作記憶體將主記憶體變數copy到工作記憶體後,再執行操作。所以,導致了多執行緒時變數資料不一致性。

      2.加鎖:鎖釋放後強制將快取資料重新整理到主記憶體。

    3. 操作原子性:加鎖。