ThreadLocal是什么?使用場景有哪些?


什么是ThreadLocal?

  ThreadLocal為每個使用該變量的線程提供獨立的變量副本,所以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應的副本。

測試代碼:

package com.javaBase.LineDistance;

/**
 * 〈一句話功能簡述〉;
 * 〈功能詳細描述〉
 *
 * @author jxx
 * @see [相關類/方法](可選)
 * @since [產品/模塊版本] (可選)
 */
public class TestThreadLocal {

    public static void main(String[] args) {

        ThreadLocal<Integer> threadLocal = new MyThreadLocal();

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 3; i++) {
                    threadLocal.set(threadLocal.get() + 1);
                    System.out.println("線程1:" + threadLocal.get());
                }
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 3; i++) {
                    threadLocal.set(threadLocal.get() + 1);
                    System.out.println("線程2:" + threadLocal.get());
                }
            }
        });

        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 3; i++) {
                    threadLocal.set(threadLocal.get() + 1);
                    System.out.println("線程3:" + threadLocal.get());
                }
            }
        });

        t1.start();
        t2.start();
        t3.start();
    }

    private static class MyThreadLocal extends ThreadLocal<Integer> {

        @Override
        protected Integer initialValue() {
            return 0;
        }
    }

}

執行結果:

線程2:1
線程1:1
線程2:2
線程3:1
線程1:2
線程3:2
線程2:3
線程3:3
線程1:3

有結果可知個線程之間對ThreadLocal的操作互不影響。

ThreadLocal原理

ThreadLocal中的幾個主要方法:

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

get和set方法源碼:

/**
     * 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);
    }

  線程隔離的秘密,就在於ThreadLocalMap這個類。ThreadLocalMap是ThreadLocal類的一個靜態內部類,它實現了鍵值對的設置和獲取(對比Map對象來理解),每個線程中都有一個獨立的ThreadLocalMap副本,它所存儲的值,只能被當前線程讀取和修改。ThreadLocal類通過操作每一個線程特有的ThreadLocalMap副本,從而實現了變量訪問在不同線程中的隔離。因為每個線程的變量都是自己特有的,完全不會有並發錯誤。還有一點就是,ThreadLocalMap存儲的鍵值對中的鍵是this對象指向的ThreadLocal對象,而值就是你所設置的對象了。

ThreadLocal的應用場景

1、方便同一個線程使用某一對象,避免不必要的參數傳遞;
2、線程間數據隔離(每個線程在自己線程里使用自己的局部變量,各線程間的ThreadLocal對象互不影響);
3、獲取數據庫連接、Session、關聯ID(比如日志的uniqueID,方便串起多個日志);
其中spring中的事務管理器就是使用的ThreadLocal:
  Spring的事務管理器通過AOP切入業務代碼,在進入業務代碼前,會依據相應的事務管理器提取出相應的事務對象,假如事務管理器是DataSourceTransactionManager,
就會從DataSource中獲取一個連接對象,通過一定的包裝后將其保存在ThreadLocal中。而且Spring也將DataSource進行了包裝,重寫了當中的getConnection()方法,或者說
該方法的返回將由Spring來控制,這樣Spring就能讓線程內多次獲取到的Connection對象是同一個。

 

參考鏈接:徹底理解ThreadLocal

       java ThreadLocal(應用場景及使用方式及原理)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM