您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關Java中ThreadLocal如何使用,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
首先,讓我們看看 ThreadLocal 類中的介紹: >This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID). > >Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the ThreadLocal instance is accessible; after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist).
按照文中所述,ThreadLocal 提供的是線程本地變量,每個線程都有一份單獨的副本,經常使用的方式是私有靜態變量
。關鍵在于下一段,線程存活,ThreadLocal 實例就可以被訪問,線程消失,就會被垃圾回收。
看到這兒,有沒有想起上一篇內容所說的引用類型
,有可能是軟引用
或者弱引用
,具體是什么呢?還是來看看代碼:
public T get() { // 獲取當前線程 Thread t = Thread.currentThread(); // 獲取線程里的map 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(); } ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
上面展示的是 ThreadLocal 中的get()
方法,關鍵的 map 是在 Thread 類中的threadLocals
變量,讓我們繼續看看 ThreadLocalMap 的源代碼:
ThreadLocal.ThreadLocalMap threadLocals = null; static class ThreadLocalMap { static class Entry extends WeakReference<threadlocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<!--?--> k, Object v) { // 使用ThreadLocal作為key,并且是弱引用 super(k); value = v; } } // 省略代碼 }
根據上一篇文章所述,如果一個對象只有弱引用
,那么當下一次 GC 進行時,該對象就會被回收。那么讓我們整理一下:
ThreadLocalMap 的 Entry 對 ThreadLocal 的引用為弱引用
。
ThreadLocal 本身并不存儲值,具體的 value 依舊在各個線程中。因此你可以把 ThreadLocal 看成一個工具類。
但需要注意的是,Entry 中,只有key是弱引用,但 value 依舊是強引用。那會不會出現 key 被垃圾回收后,這個 map 的 key 為 null,但 value 依舊存在的情況呢?
確實是有可能的,但 JDK 本身也做了優化,可以看看 ThreadLocalMap 的 set()方法:
private void set(ThreadLocal<!--?--> key, Object value) { // We don't use a fast path as with get() because it is at // least as common to use set() to create new entries as // it is to replace existing ones, in which case, a fast // path would fail more often than not. Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len-1); for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { ThreadLocal<!--?--> k = e.get(); if (k == key) { e.value = value; return; } if (k == null) { replaceStaleEntry(key, value, i); return; } } tab[i] = new Entry(key, value); int sz = ++size; if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }
調用 set()的時候,ThreadLocalMap 檢查到 key 為 null 的 entry 時,會將 value 也設置為 null,這樣 value 之前對應的實例也可以被回收。
先讓我們看一個簡單的例子:
public class ThreadLocalSimpleDemo { public static void main(String[] args) { int threads = 3; InnerClass innerClass = new InnerClass(); for (int i = 1; i <= threads; i++) { new Thread(() -> { for (int j = 0; j < 4; j++) { innerClass.add(String.valueOf(j)); innerClass.print(); } innerClass.set("hello world"); }, "thread - " + i).start(); } } private static class InnerClass { /** * 添加 */ public void add(String newStr) { StringBuilder str = Counter.counter.get(); Counter.counter.set(str.append(newStr)); } /** * 打印 */ public void print() { System.out.printf( "Thread name:%s , ThreadLocal hashcode:%s, Instance hashcode:%s, Value:%s\n", Thread.currentThread().getName(), Counter.counter.hashCode(), Counter.counter.get().hashCode(), Counter.counter.get().toString() ); } /** * 賦值 */ public void set(String words) { Counter.counter.set(new StringBuilder(words)); System.out.printf( "Set, Thread name:%s , ThreadLocal hashcode:%s, Instance hashcode:%s, Value:%s\n", Thread.currentThread().getName(), Counter.counter.hashCode(), Counter.counter.get().hashCode(), Counter.counter.get().toString() ); } } private static class Counter { /** * 初始化時是一個空的StringBuilder對象 */ private static ThreadLocal<stringbuilder> counter = ThreadLocal.withInitial(StringBuilder::new); } }
其打印結果為:
Thread name:thread - 3 , ThreadLocal hashcode:310471657, Instance hashcode:640658548, Value:0 Thread name:thread - 2 , ThreadLocal hashcode:310471657, Instance hashcode:126253473, Value:0 Thread name:thread - 2 , ThreadLocal hashcode:310471657, Instance hashcode:126253473, Value:01 Thread name:thread - 2 , ThreadLocal hashcode:310471657, Instance hashcode:126253473, Value:012 Thread name:thread - 2 , ThreadLocal hashcode:310471657, Instance hashcode:126253473, Value:0123 Thread name:thread - 1 , ThreadLocal hashcode:310471657, Instance hashcode:829132711, Value:0 Thread name:thread - 1 , ThreadLocal hashcode:310471657, Instance hashcode:829132711, Value:01 Thread name:thread - 1 , ThreadLocal hashcode:310471657, Instance hashcode:829132711, Value:012 Thread name:thread - 1 , ThreadLocal hashcode:310471657, Instance hashcode:829132711, Value:0123 Set, Thread name:thread - 1 , ThreadLocal hashcode:310471657, Instance hashcode:820066274, Value:hello world Thread name:thread - 3 , ThreadLocal hashcode:310471657, Instance hashcode:640658548, Value:01 Thread name:thread - 3 , ThreadLocal hashcode:310471657, Instance hashcode:640658548, Value:012 Set, Thread name:thread - 2 , ThreadLocal hashcode:310471657, Instance hashcode:155293473, Value:hello world Thread name:thread - 3 , ThreadLocal hashcode:310471657, Instance hashcode:640658548, Value:0123 Set, Thread name:thread - 3 , ThreadLocal hashcode:310471657, Instance hashcode:1804272849, Value:hello world
可以看出,我們在使用 ThreadLocal 時,用的是同一個對象,但各個線程對應的實例是不一樣的。而在調用 set() 方法后,對應的實例會被替換。
對于 Java Web 應用而言,Session 保存了很多信息。很多時候需要通過 Session 獲取信息,有些時候又需要修改 Session 的信息。一方面,需要保證每個線程有自己單獨的 Session 實例。另一方面,由于很多地方都需要操作 Session,存在多方法共享 Session 的需求。使用 ThreadLocal 進行實現:
public class SessionHandler { public static ThreadLocal<session> session = ThreadLocal.<session>withInitial(() -> new Session()); @Data public static class Session { private String id; private String user; private String status; } public String getUser() { return session.get().getUser(); } public String getStatus() { return session.get().getStatus(); } public void setStatus(String status) { session.get().setStatus(status); } public static void main(String[] args) { new Thread(() -> { SessionHandler handler = new SessionHandler(); handler.getStatus(); handler.getUser(); handler.setStatus("close"); handler.getStatus(); }).start(); } }
關于Java中ThreadLocal如何使用就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。