您好,登錄后才能下訂單哦!
Java中怎么實現線程封閉,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
當訪問共享變量時,往往需要加鎖來保證數據同步。一種避免使用同步的方式就是不共享數據。如果僅在單線程中訪問數據,就不需要同步了。這種技術稱為線程封閉。在Java語言中,提供了一些類庫和機制來維護線程的封閉性,例如局部變量和ThreadLocal類,本文主要深入講解如何使用ThreadLocal類來保證線程封閉。
ThreadLocal類能使線程中的某個值與保存值的對象關聯起來,它提供了get、set方法,這些方法為每個使用該變量的線程保存一份獨立的副本,因此get總是set當前線程的set最新值。
首先我們來看個例子,這個例子來自于http://www.cnblogs.com/dolphin0520/p/3920407.html
public class Test1 { ThreadLocal<Long> longLocal = new ThreadLocal<Long>(); ThreadLocal<String> stringLocal = new ThreadLocal<String>(); public void set() { longLocal.set(Thread.currentThread().getId()); stringLocal.set(Thread.currentThread().getName()); } public long getLong() { return longLocal.get(); } public String getString() { return stringLocal.get(); } public static void main(String[] args) throws InterruptedException { final Test1 test = new Test1(); test.set(); System.out.println(test.getLong()); System.out.println(test.getString()); Thread thread1 = new Thread(() -> { test.set(); System.out.println(test.getLong()); System.out.println(test.getString()); }); thread1.start(); thread1.join(); System.out.println(test.getLong()); System.out.println(test.getString()); } }
運行該程序,代碼輸出的結果為:
1
main
10
Thread-0
1
main
從這段代碼可以看出在mian線程和thread1線程確實都保存著各自的副本,它們的副本各自不干擾。
來從源碼的角度來解析ThreadLocal這個類,這個類存放在java.lang包,這個類有很多方法。
它內部又個ThreadLocalMap類,主要有set()、get()、setInitialValue 等方法。
首先來看下set方法,獲取當前Thread的 map,如果不存在則新建一個并設置值,如果存在設置值,源碼如下:
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
跟蹤createMap,可以發現它根據Thread創建來一個ThreadLocalMap。
void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
t.threadLocals為當前線程的一個變量,也就是ThreadLocal的數據都是存放在當前線程的threadLocals變量里面的,由此可見用ThreadLocal存放的數據是線程安全的。因為它對于不同的線程來,使用ThreadLocal的set方法都會根據線程判斷該線程是否存在它的threadLocals成員變量,如果沒有就建一個,有的話就存下數據。
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocalMap為ThreadLocal的一個內部類,源碼如下:
static class ThreadLocalMap { static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } }
可以看到ThreadLocalMap的Entry繼承了WeakReference,并且使用ThreadLocal作為鍵值。
在使用ThreadLocal的get方法之前一定要先set,要不然會報空指針異常。還有一種方式就是在初始化的時候調用initialValue()方法賦值。改造下之前的例子,代碼如下:
public class Test2 { ThreadLocal<Long> longLocal = new ThreadLocal<Long>(){ @Override protected Long initialValue() { return Thread.currentThread().getId(); } }; ThreadLocal<String> stringLocal = new ThreadLocal<String>(){ @Override protected String initialValue() { return Thread.currentThread().getName(); } }; public long getLong() { return longLocal.get(); } public String getString() { return stringLocal.get(); } public static void main(String[] args) throws InterruptedException { final Test2 test = new Test2(); System.out.println(test.getLong()); System.out.println(test.getString()); Thread thread1 = new Thread(() -> { System.out.println(test.getLong()); System.out.println(test.getString()); }); thread1.start(); thread1.join(); System.out.println(test.getLong()); System.out.println(test.getString()); } }
運行該程序,代碼輸出的結果為:
1
main
10
Thread-0
1
main
通常講JDBC連接保存在ThreadLocal對象中,每個對象都有屬于自己的連接,代碼如下:
private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() { public Connection initialValue() { return DriverManager.getConnection(DB_URL); } }; public static Connection getConnection() { return connectionHolder.get(); }
關于Java中怎么實現線程封閉問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。