Java基礎【四】 - 經常使用類庫:java.lang.ThreadLocal

使用場景

ThreadLocal和synchronized都是爲了解決多線程中相同變量的訪問衝突問題。synchronized保證同一時刻只有一個線程對共享變量進行操做,synchronized是時間換空間的體現。ThreadLocal使變量在每一個線程中都有獨立拷貝的共享變量,不會出現一個線程讀取變量時而被另外一個線程修改的現象。ThreadLocal是編程中空間換時間的體現java

ThreadLocal的內部結構

一、每一個Thread線程內部都有一個Map。
二、Map裏面存儲線程本地對象(key)和線程的變量副本(value)。
三、每一個Thread線程內部都有一個Map。
四、Thread對於不一樣的線程,每次獲取副本值時,別的線程並不能獲取到當前線程的副本值,造成了副本的隔離,互不干擾。編程

ThreadLocal的內部結構圖

圖片描述
從上圖能夠看到每一個線程,ThreadLocal內部都有一個Map來存儲當前線程的共享變量,只能當前線程訪問,從而保證不會影響其餘線程的變量。多線程

ThreadLocal核心方法

get()方法用於獲取當前線程的副本變量值。
set()方法用於保存當前線程的副本變量值。
initialValue()爲當前線程初始副本變量值。
remove()方法移除當前前程的副本變量值。併發

ThreadLocal使用實例

//包含業務惟一標識的類
public class Context {
    private String transactionId;
 
    public String getTransactionId() {
        return transactionId;
    }
 
    public void setTransactionId(String transactionId) {
        this.transactionId = transactionId;
    }
 
}

// 其中引用了Context類
public class MyThreadLocal {
    private static final ThreadLocal<Context> userThreadLocal = new ThreadLocal<Context>();
    public static void set(Context user){
        userThreadLocal.set(user);
    }
    public static void unset(){
        userThreadLocal.remove();
    }
    public static Context get(){
        return userThreadLocal.get();
    }
    
}

//ThreadLocalDemo.java。生成並將業務標識設置到ThreadLocal中而後在業務方法中調用
public class ThreadLocalDemo implements Runnable{
    private static AtomicInteger ai = new AtomicInteger(0);
    public void run() {
        Context context = new Context();
        context.setTransactionId(getName());
        //設置線程變量
        MyThreadLocal.set(context);
        System.out.println("request["+Thread.currentThread().getName()+"]:"+context.getTransactionId());
        new BusinessService().businessMethod();
        MyThreadLocal.unset();
    }
    
    private String getName() {
        return ai.getAndIncrement()+"";
    }
 
    public static void main(String[] args) {
        ThreadLocalDemo tld  = new ThreadLocalDemo();
        new Thread(tld).start();
        new Thread(tld).start();
    }
 
}
public class BusinessService {
 
    public void businessMethod() {
        Context context = MyThreadLocal.get();
        System.out.println("service["+Thread.currentThread().getName()+"]:"+context.getTransactionId());
    }
 
}

ThreadLocal使用總結

一、每一個ThreadLocal只能保存一個變量副本,若是想要上線一個線程可以保存多個副本以上,就須要建立多個ThreadLocal。
二、ThreadLocal內部的ThreadLocalMap鍵爲弱引用,會有內存泄漏的風險。
三、ThreadLocal適用於無狀態,副本變量獨立後不影響業務邏輯的高併發場景。若是若是業務邏輯強依賴於副本變量,則不適合用ThreadLocal解決,須要另尋解決方案。高併發

若是內容對你有幫助但願點贊收藏謝謝!!!this

相關文章
相關標籤/搜索