JAVA線程8 - ThreadLocal

1、ThreadLocal簡介

ThreadLocal並非一個Thread,而是Thread的局部變量。
當使用ThreadLocal維護變量時,ThreadLocal爲每一個使用該變量的線程提供獨立的變量副本,因此每個線程均可以獨立地改變本身的副本,而不會影響其它線程所對應的副本。

2、爲何會出現ThreadLocal類

在java多線程併發訪問共享變量時,爲了線程安全,咱們的作法多是對多個線程訪問進行同步控制。可是這樣會下降系統的性能。在JDK 1.2的版本中就提供java.lang.ThreadLocal類,它爲每個線程都維護了一個變量,每一個線程都有了本身獨立的變量後,就沒有了併發下的線程安全問題。ThreadLocal類是以空間來換取線程安全的一種策略。

3、ThreadLocal原理

ThreadLocal爲每個線程維護變量的副本實現思路:
在ThreadLocal類中有一個Map,用於存儲每個線程的變量副本,Map中元素的鍵爲線程對象,而值對應線程的變量副本。

4、實現一個簡單的ThreadLocal

SimpleThreadLocal.java
public class SimpleThreadLocal {
    private Map valueMap = Collections.synchronizedMap(new HashMap());

    public void set(Object newValue) {
        valueMap.put(Thread.currentThread(), newValue);	//鍵爲線程對象,值爲本線程的變量副本
    }

    public Object get() {
        Thread currentThread = Thread.currentThread();
        Object o = valueMap.get(currentThread);	//返回本線程對應的變量
        if (o == null && !valueMap.containsKey(currentThread)) {	//若是在Map中不存在,放到Map中保存起來。
            o = initialValue();
            valueMap.put(currentThread, o);
        }
        return o;
    }

    public void remove() {
        valueMap.remove(Thread.currentThread());
    }

    public Object initialValue() {
        return null;
    }
}

5、API介紹

void set(Object value)
   設置當前線程的線程局部變量的值;
public Object get()
   該方法返回當前線程所對應的線程局部變量;
public void remove()
   將當前線程局部變量的值刪除,目的是爲了減小內存的佔用,該方法是JDK 5.0新增的方法。須要指出的是,當線程結束後,對應該線程的局部變量將自動被垃圾回收,因此顯式調用該方法清除線程的局部變量並非必須的操做,但它能夠加快內存回收的速度;
protected Object initialValue()
   返回該線程局部變量的初始值,該方法是一個protected的方法,顯然是爲了讓子類覆蓋而設計的。這個方法是一個延遲調用方法,在線程第1次調用get()或set(Object)時才執行,而且僅執行1次。ThreadLocal中的默認實現直接返回一個null。 


在JDK5.0中,ThreadLocal已經支持泛型,該類的類名已經變爲ThreadLocal<T>。API方法也相應進行了調整,新版本的API方法分別是void set(T value)、T get()以及T initialValue()。 

6、ThreadLocal使用

看一個運用ThreadLocal來實現數據庫鏈接Connection對象線程隔離的例子
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class ConnectionManager {

    private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() {
        @Override
        protected Connection initialValue() {
            Connection conn = null;
            try {
                conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "username","password");
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return conn;
        }
    };

    public static Connection getConnection() {
        return connectionHolder.get();
    }

    public static void setConnection(Connection conn) {
        connectionHolder.set(conn);
    }
}
說明:經過調用ConnectionManager.getConnection()方法,每一個線程獲取到的,都是和當前線程綁定的那個Connection對象,第一次獲取時,是經過initialValue()方法的返回值來設置值的。經過ConnectionManager.setConnection(Connection conn)方法設置的Connection對象,也只會和當前線程綁定。這樣就實現了Connection對象在多個線程中的徹底隔離。在Spring容器中管理多線程環境下的Connection對象時,採用的思路和以上代碼很是類似。

7、線程同步和ThreadLocal的比較

對於多線程資源共享的問題,同步機制採用了「以時間換空間」的方式,而ThreadLocal採用了「以空間換時間」的方式。前者僅提供一份變量,讓不一樣的線程排隊訪問,然後者爲每個線程都提供了一份變量,所以能夠同時訪問而互不影響。 Synchronized用於線程間的數據共享,而ThreadLocal則用於線程間的數據隔離。 ThreadLocal並不能替代synchronized,它們處理不一樣的問題域。Synchronized用於實現同步機制,比ThreadLocal更加複雜。 參考資料: http://my.oschina.net/lichhao/blog/111362 http://stamen.iteye.com/blog/1535120
相關文章
相關標籤/搜索