Java并發(fā)編程之threadLocal
1、ThreadLocal介紹
多個線程訪問同一個共享變量時特別容易出現(xiàn)并發(fā)問題,特別是多線程需要對共享變量進行寫入時。為了保證線程安全,一般使用者在訪問共享變量的時候需要進行適當?shù)耐?,如圖

同步的一般措施是加鎖,這就需要使用者對鎖有一定的了解,這顯然加重了使用者的負擔,那么有沒有一種方法可以做到,當創(chuàng)建一個變量后,每個線程對其進行訪問的時候訪問的是自己線程的變量呢?其實ThreadLocal就可以做到。
ThreadLocal是JDK包提供的,它提供了線程本地變量,也就是說如果創(chuàng)建了一個ThreadLocal變量,那么訪問這個變量的每一個線程都會有這個變量的一個本地副本。當多線程操作這個變量的時候,實際操作的就是自己本地內存的里面的里面的變量,從而避免了線程安全問題。創(chuàng)建一個ThreadLocal變量后,每一個線程都會復制一個變量到自己的本地內存。

2、ThreadLocal使用實例
package com.heiye.learn1;
public class ThreadLocalTest {
//print方法
static void print(String threadName) {
//打印當前線程本地內存中LocalVariable變量的值
System.out.println(threadName + ":" + localVariable.get());
//清除當前線程本地內存中的localVariable變量值
//localVariable.remove();
}
//創(chuàng)建ThreadLocal變量
static ThreadLocal<String> localVariable = new ThreadLocal<>();
public static void main(String[] args) {
//創(chuàng)建線程threadOne
Thread threadOne = new Thread(new Runnable() {
@Override
public void run() {
//設置線程one變量localVariable值
localVariable.set("threadOne local Variable");
//調用打印函數(shù)
print("threadOne");
//打印本地變量值
System.out.println("threadOne remove after" + ":" + localVariable.get());
}
});
//創(chuàng)建線程threadTwo
Thread threadTwo = new Thread(new Runnable() {
@Override
public void run() {
//設置線程two變量localVariable值
localVariable.set("threadTwo local Variable");
//調用打印函數(shù)
print("threadTwo");
//打印本地變量值
System.out.println("threadTwo remove after" + ":" + localVariable.get());
}
});
threadOne.start();
threadTwo.start();
}
}

線程one首先通過set()方法為threadLocal變量設置了一個值,這其實設置的就是線程one本地內存中對于threadLocal變量的一個副本。這個副本是線程two訪問不了的。
如果清除當前線程本地內存中的localVariable變量值,也就是執(zhí)行localVariable.remove() ;則:

3、ThreadLocal實現(xiàn)原理
首先查看一下ThreadLocal類圖結構

由該圖可知,Thread類有一個ThreadLocals和inheritableThreadLocals,它們都是ThreadLocalMap類型的變量,而ThreadLocalMap是一個定制化的HashMap。在默認的情況下,每個線程中的兩個變量都為null,只有當?shù)谝粋€線程調用ThreadLocal的set或者get方法時才會創(chuàng)建它們,其實每個線程得到本地變量不是存放在ThreadLocal實例里面,而是存放在具體的線程內存空間里。ThreadLocal就是一個工具殼,它通過set方法把value值存放在調用線程的threadlocals里面并存放起來,當調用線程調用它的get()方法的時候,再從當前線程的threadLocals變量里面將其拿出來使用。
分析set,get,remove邏輯
//set
public void set(T value) {
//獲取當前線程
Thread t = Thread.currentThread();
//將當前線程作為key,到ThreadLocalMap取查找對應的線程變量
ThreadLocalMap map = getMap(t);
//如果找到,則設置
if (map != null)
map.set(this, value);
else //第一次調用就創(chuàng)建當前線程所在的hashmap
createMap(t, value);
}
//get
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();
}
//remove
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
在每一個線程內都有一個名為threadLocals的成員變量,該變量的類型為HashMap,其中的key為我們定義的threadLocal變量的this引用,value為我們使用set方法設置的值。每個線程的本地變量存放在自己的內存變量ThreadLocals中,如果當前線程一直不消亡,那么這些本地變量會一直存在,所有可能造成內存溢出。
到此這篇關于Java并發(fā)編程 threadLocal 的文章就介紹到這了,更多相關threadLocal 內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Spring rest接口中的LocalDateTime日期類型轉時間戳
這篇文章主要介紹了Spring rest接口中的LocalDateTime日期類型轉時間戳的方法,Java程序中一般將日期類型定義為LocalDateTime,數(shù)據(jù)庫中保存的時間是0時區(qū)的時間2023-03-03
springboot結合mysql主從來實現(xiàn)讀寫分離的方法示例
這篇文章主要介紹了springboot結合mysql主從來實現(xiàn)讀寫分離的方法示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-04-04
springboot后端如何實現(xiàn)攜帶token登陸
這篇文章主要介紹了springboot后端如何實現(xiàn)攜帶token登陸,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-11-11
Spring Boot2.0中SpringWebContext找不到無法使用的解決方法
這篇文章主要給大家介紹了關于Spring Boot2.0中SpringWebContext找不到無法使用的解決方法,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面隨著小編來一起學習學習吧2018-12-12
Java使用黑盒方式模擬實現(xiàn)內網(wǎng)穿透
這篇文章主要介紹了Java使用黑盒方式模擬實現(xiàn)內網(wǎng)穿透,內網(wǎng)穿透,也即 NAT 穿透,進行 NAT 穿透是為了使具有某一個特定源 IP 地址和源端口號的數(shù)據(jù)包不被 NAT 設備屏蔽而正確路由到內網(wǎng)主機,需要的朋友可以參考下2023-05-05
SpringCloud maven-assembly-plugin 多級目錄打包的實現(xiàn)
本文主要介紹了SpringCloud maven-assembly-plugin 多級目錄打包的實現(xiàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-10-10

