1. 程式人生 > >【執行緒】 ThreadLocal

【執行緒】 ThreadLocal

       ThreadLocal類提供了執行緒區域性變數。這些變數不同於他們的普通對應物,因為訪問一個變數(通過getset方法)的每個執行緒都有自己的區域性變數,它獨立於變數的初始化副本。ThreadLocal例項通常是類中的私有靜態欄位,他們希望將狀態與某一個執行緒(例如,使用者ID或事務ID)相關聯。

簡單說,ThreadLocal的作用是提供執行緒內的區域性變數,這種變數線上程的生命週期內起作用,只要在本執行緒內,隨時隨地可以取,於是也就隔離了其他執行緒,主要解決多執行緒中資料資料因併發產生不一致問題,以此保證執行緒安全

先來看一下

ThreadLocal類的方法。


  initialValue()方法:返回該執行緒區域性變數的初始值,是一個延遲呼叫方法,線上程第1次呼叫get()set(Object)時才執行,並且僅執行1次。

    protected T initialValue() {
        return null;
    }

get()方法:返回當前執行緒所對應的執行緒區域性變數。

/**
     * Returns the value in the currentthread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initializedto the value returned
     * by an invocation of the {@link#initialValue} method.
     *
     * @return the current thread's value ofthis 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)
                return (T)e.value;
        }
        return setInitialValue();
    }

set()方法:設定當前執行緒的執行緒區域性變數的值。

/**
     * Returns the value in the currentthread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initializedto the value returned
     * by an invocation of the {@link#initialValue} method.
     *
     * @return the current thread's value ofthis 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)
                return (T)e.value;
        }
        return setInitialValue();
    }

remove()方法:將當前執行緒區域性變數的值刪除,目的是為了減少記憶體的佔用

    /**
     * Removes the current thread's value forthis thread-local
     * variable.  If this thread-local variable is subsequently
     * {@linkplain #get read} by the currentthread, its value will be
     * reinitialized by invoking its {@link#initialValue} method,
     * unless its value is {@linkplain #setset} by the current thread
     * in the interim.  This may result in multiple invocations ofthe
     * <tt>initialValue</tt> methodin the current thread.
     *
     * @since 1.5
     */
     public void remove() {
         ThreadLocalMap m =getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }
【實踐】

ThreadLocal測試:

package com.bjpowernode.drp.util;

/**
 * ThreadLocal測試
 * 
 * @author happy
 * 
 */
public class TestThreadLocal {
	// 通過匿名內部類覆蓋ThreadLocal的initialValue()方法,指定初始值
	private static ThreadLocal<Integer> threadNum = new ThreadLocal<Integer>() {
		public Integer initialValue() {
			return 0;
		}
	};

	// 獲取下一個序列值
	public int getNextNum() {
		threadNum.set(threadNum.get() + 1);
		return threadNum.get();
	}

	private static class TestClient extends Thread {
		private TestThreadLocal testThreadLocal;

		public TestClient(TestThreadLocal testThreadLocal) {
			this.testThreadLocal = testThreadLocal;
		}

		public void run() {
			for (int i = 0; i < 3; i++) {
				System.out.println("執行緒[" + Thread.currentThread().getName()
						+ "] --> [" + testThreadLocal.getNextNum() + "]");
			}
		}

		public static void main(String[] args) {
			TestThreadLocal test = new TestThreadLocal();
			// 建立 3個執行緒,共享testThreadLocal,但各自產生序列號
			TestClient t1 = new TestClient(test);
			TestClient t2 = new TestClient(test);
			TestClient t3 = new TestClient(test);
			t1.start();
			t2.start();
			t3.start();
		}
	}
}
drp中的例項:

        不同的執行緒在使用ConnectionManager時,先判斷connectionHolder.get()是否是null,如果是null,則說明當前執行緒還沒有對應的Connection物件,這時建立一個Connection物件並新增到本地執行緒變數中;如果不為null,則說明當前的執行緒已經擁有了Connection物件,直接使用就可以了。這樣,就保證了不同的執行緒使用執行緒相關的Connection,而不會使用其它執行緒的Connection。因此,這個connectionHolder.get()就可以做到在本執行緒內共享了。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * 採用ThreadLocal封裝Connection
 * 
 * @author Administrator
 * 
 */
public class ConnectionManager {

	private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>();

	/**
	 * 得到Connection
	 * 
	 * @return
	 */
	public static Connection getConnection() {
		// 先從執行緒變數中獲取connection
		Connection conn = connectionHolder.get();
		// 如果在當前執行緒中沒有繫結相應的Connection
		if (conn == null) {
			try {
				JdbcConfig jdbcConfig = XmlConfigReader.getInstance()
						.getJdbcConfig();
				Class.forName(jdbcConfig.getDriverName());
				conn = DriverManager.getConnection(jdbcConfig.getUrl(),
						jdbcConfig.getUserName(), jdbcConfig.getPassword());
				// 將Connection設定到ThreadLocal
				connectionHolder.set(conn);
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
				throw new ApplicationException("系統錯誤,請聯絡系統管理員");
			} catch (SQLException e) {
				e.printStackTrace();
				throw new ApplicationException("系統錯誤,請聯絡系統管理員");
			}
		}
		return conn;
	}

	/**
	 * 關掉connection
	 */
	public static void closeConnection() {
		Connection conn = connectionHolder.get();
		if (conn != null) {
			try {
				conn.close();
				// 從ThreadLocal中清除Connection
				connectionHolder.remove();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}

【比較】

  ThreadLocal和Synchonized都用於解決多執行緒併發訪問。synchronized是利用鎖的機制,使變數或程式碼塊在某一時該只能被一個執行緒訪問。而ThreadLocal為每一個執行緒都提供了變數的副本,使得每個執行緒在某一時間訪問到的是各自相應的物件,這樣就隔離了多個執行緒對資料的資料共享。

     Synchronized用於執行緒間的資料共享,而ThreadLocal則用於執行緒間的資料隔離。