成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

java中ThreadLocal有什么作用

這篇文章主要介紹“java中ThreadLocal有什么作用”,在日常操作中,相信很多人在java中ThreadLocal有什么作用問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”java中ThreadLocal有什么作用”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

創(chuàng)新互聯(lián)是一家集網(wǎng)站建設(shè),故城企業(yè)網(wǎng)站建設(shè),故城品牌網(wǎng)站建設(shè),網(wǎng)站定制,故城網(wǎng)站建設(shè)報價,網(wǎng)絡(luò)營銷,網(wǎng)絡(luò)優(yōu)化,故城網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強企業(yè)競爭力??沙浞譂M足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時我們時刻保持專業(yè)、時尚、前沿,時刻以成就客戶成長自我,堅持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實用型網(wǎng)站。

談一談不常見卻又不可少的ThreadLocal

內(nèi)存模型中所有變量存儲在主內(nèi)存中,當(dāng)一個線程中要使用某個變量時,需要從主內(nèi)存復(fù)制該變量到其線程內(nèi)才能操作,此時線程中操作的是主內(nèi)存變量的副本,操作完成后再刷回主內(nèi)存。刷回的實質(zhì)就是變量賦值

如果多個線程訪問同一個變量時,每個線程都具有一個副本,操作完畢后都會刷回主內(nèi)存,刷回時間存在先后,則賦值有先后,當(dāng)然后者會覆蓋前者,這是造成可見性問題的次要原因。

引入以上知識點后,再來說明ThreadLocal。一個線程想使用某個變量,于是從主內(nèi)存復(fù)制該共享變量到線程內(nèi)部中。使用完畢后想再下次再次使用該變量時,得到的變量副本是上次使用的副本,而不是從主內(nèi)存的變量再次復(fù)制過來的副本,并且不想讓其他線程影響到該變量。這就是ThreadLocal的目的,其實現(xiàn)不是通過共享變量這種方式實現(xiàn)的,詳細(xì)內(nèi)容下面介紹

目的很明確,但是身處JAVA內(nèi)存模型中要遵循內(nèi)存模型規(guī)范,下面看看JDK是如何即滿足內(nèi)存模型規(guī)范,又滿足ThreadLocal目的。

滿足內(nèi)存模型

這點很簡單,就是你該怎么樣還怎么樣,仍然受你管轄,該復(fù)制就復(fù)制,該刷回就刷回,不可見還是會造成不可見。

滿足ThreadLocal目的

多個線程都能訪問的變量才叫共享變量,如果控制變量的訪問方式,使其他線程線程不能訪問就可以了??刂品绞骄褪菍⒕€程與變量的一一對應(yīng),將該變量的訪問入口控制到只有該線程即可,JDK中的做法就是讓線程持有這個變量(綁定到線程本身)

線程可以綁定變量,但是并不知道需要綁定多少個,于是將這個存儲功能還是交給專門的數(shù)據(jù)結(jié)構(gòu)—>Map。并且還專門設(shè)計了一個用來訪問這個Map的工具,這個工具就是ThreadLocal。并且這個Map的key為ThreadLocal實例的引用地址,value存儲真正的變量。

這樣設(shè)計就到達目的了。ThreadLocal構(gòu)建時接收個泛型告訴你存儲的變量是一個對象類型。

ThreadLocal設(shè)計

核心Map設(shè)計

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;
        }
    }
...
}

這里Map的Entry被設(shè)計為弱引用。

內(nèi)存存儲圖示如下

java中ThreadLocal有什么作用

弱引用WeakReference

WeakReference是Java語言規(guī)范中為了區(qū)別直接的對象引用(程序中通過構(gòu)造函數(shù)聲明出來的對象引用)而定義的另外一種引用關(guān)系。WeakReference標(biāo)志性的特點是:reference實例不會影響到被應(yīng)用對象的GC回收行為(即只要對象被除WeakReference對象之外所有的對象解除引用后,該對象便可以被GC回收),只不過在被對象回收之后,reference實例想獲得被應(yīng)用的對象時程序會返回null。

這里使用弱引用的原因:ThreadLocal目的就是達到變量只能自己訪問別的線程不能訪問的目的,Map設(shè)計的key為ThreadLocal實例的引用地址,value為變量,當(dāng)ThreadLocal定義處的實例被銷毀時,如果Map中Entity還強引用該實例時,就會導(dǎo)致該實例不會被回收,而外部因為引用對象被銷毀而不能操作該地址,于是會造成內(nèi)存泄露。當(dāng)Map的Key設(shè)計為弱引用時,如果外部實例被銷毀,即使弱引用還存在,Map中的對應(yīng)的ThreadLocal也會被回收,則key=null。

使用弱引用后,雖然Key可以被回收了,但是value還是存在的,還會存在臨時內(nèi)存泄露問題,圖示如下

臨時內(nèi)存泄露圖示

java中ThreadLocal有什么作用

對于臨時內(nèi)存泄露,ThreadLocal在后續(xù)get、set操作時,會清理掉Map中key=null的Entity節(jié)點,不過再清理之前Value一直存在,如果繼續(xù)使用Value很容易出現(xiàn)問題

ThreadLocal由于一個實例存儲一個對象,因此一般會被定義為static的,防止ThreadLocal所在類實例被多次創(chuàng)建時ThreadLocal也被多次創(chuàng)建,

另外ThreadLocal也會定義為final的,防止實例被覆蓋掉,也就是只在實例化時new一次

Map的持有狀態(tài)

在Thread類定義中這樣定義ThreadLocalMap

public class Thread implements Runnable {
  ...
  ThreadLocal.ThreadLocalMap threadLocals = null;
  ...
}

也就是ThreadLocalMap是線程類Thread持有的對象,一個線程持有一個ThreadLocalMap,只有線程存在,則ThreadLocalMap必存在。這也是ThreadLocal對象銷毀后,value還存在的原因,ThreadLocalMap還被Thread強引用。只有線程銷毀時,ThreadLocalMap才會隨之銷毀。
 

ThreadLocal的使用

private static final ThreadLocal<Integer> threadLocal = new ThreadLocal<>();  // 接收泛型
threadLocal.set(1);  // 對應(yīng)泛型
Integer var=threadLocal.get();

ThreadLocal底層原理

設(shè)置變量 Set

public void set(T value) {
    // 獲取當(dāng)前線程
    Thread t = Thread.currentThread();
// 從當(dāng)前線程中獲取一個ThreadLocalMap實例
    ThreadLocalMap map = getMap(t);
// map存在則放入key,value  key為當(dāng)前ThreadLocal對象引用
    if (map != null)
        map.set(this, value);
    // map不存在則構(gòu)建,同樣放入key,value  
    else
        createMap(t, value);
}

ThreadLocalMap實例的獲取就是從Thread中獲取的,也就是上面的持有狀態(tài),拿到之后就可以向Map結(jié)構(gòu)中存儲key,value了,前面也說過了,這里key存在的ThreadLocal實例的引用地址,value存在的變量的引用地址。一個線程可以存儲多個ThreadLocal實例。

getMap(Thread t)
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

就是獲取當(dāng)前線程持有的ThreadLocalMap

createMap(Thread t, T firstValue)
void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

給當(dāng)前線程初始化ThreadLocalMap實例,并set初始值。key為ThreadLocal實例的引用地址

獲取變量Get

public T get() {
    Thread t = Thread.currentThread();  //① 獲取當(dāng)前線程t
    ThreadLocalMap map = getMap(t); //②從當(dāng)前線程中獲取一個ThreadLocalMap
    if (map != null) { //③ ThreadLocalMap不為null則從map中獲取value ;     
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();//④ThreadLocalMap為null則調(diào)用setInitialValue() 返回—初始化的值
}

在最后一個return之前的代碼就是從當(dāng)前線程的ThreadLocalMap中通過Map的get方法獲取變量引用的過程。這是Map的基本用用法。

存在以下情況則需要獲取初始的默認(rèn)值,這是一個對外開放的功能,就是可以指定ThreadLocal對應(yīng)的變量的初始默認(rèn)值,默認(rèn)為null,可以被重寫

  • ThreadLocalMap未實例化

  • ThreadLocalMap已實例化,但是還沒有Set變量

設(shè)置默認(rèn)的初始值
protected T initialValue() {
    return null;
}
private T setInitialValue() {
    T value = initialValue(); //① 初始化為null
    Thread t = Thread.currentThread(); //② 獲取當(dāng)前線程
    ThreadLocalMap map = getMap(t); //③ 從當(dāng)前線程中獲取一個ThreadLocalMap
    if (map != null)
        map.set(this, value); //④ ThreadLocalMap不為null,則存儲key=this,value=value
    else
        createMap(t, value);//⑤ ThreadLocalMap為null,則為當(dāng)前線程創(chuàng)建一個ThreadLocalMap并初始化值
    return value;
}
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

代碼流程也很簡單,只是為了功能優(yōu)化了,進行的統(tǒng)一封裝

  • ThreadLocalMap的惰性實例化

  • 獲取ThreadLocalMap時需要Thread,而ThreadLocal只是訪問控制工具,于是需要打通Thread來獲取ThreadLocalMap

移除
public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
        m.remove(this);
}

主動移除是個好的處理方式,在不使用變量時應(yīng)該主動移除

到這里ThreadLocal本身的功能已經(jīng)介紹完了,可以理解為變量訪問工具,這個變量的訪問被控制到只能當(dāng)前線程有權(quán)限訪問,其他線程無權(quán)限。

代碼示例

簡單示例

package cn.tinyice.demo.thread;

import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
* ThreadLocalDemo
*
* @author Tinyice
*/
public class ThreadLocalDemo {

    private static final ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
    private static final Random random=new Random();

    /**
     * 內(nèi)存模型示例
     */
    private int mod=0;

    public ThreadLocalDemo(int mod) {
        this.mod = mod;
    }

    public int getMod() {
        return mod;
    }

    public void add() {
        mod+=1;
        // 每個線程的值是不一樣的
        threadLocal.set(get() + random.nextInt(10));
    }

    public Integer get() {
        Integer integer = threadLocal.get();
        return null == integer ? 0 : integer;
    }

    public static void main(String[] args) {
        ThreadLocalDemo threadLocalDemo = new ThreadLocalDemo(0);
        for (int i = 0; i < 1000; i++) {
            new ThreadLocalThread(threadLocalDemo).start();
        }
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("------------------------------------------------");
        System.out.println(String.format("threadId=%d threadLocal value=%d mod=%d", Thread.currentThread().getId(), threadLocalDemo.get(),threadLocalDemo.getMod()));
    }

}

class ThreadLocalThread extends Thread {

    private ThreadLocalDemo threadLocalDemo;

    public ThreadLocalThread( ThreadLocalDemo threadLocalDemo) {
        this.threadLocalDemo = threadLocalDemo;
    }

    @Override
    public void run() {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        threadLocalDemo.add();
        System.out.println(String.format("threadId=%d threadLocal value=%d", Thread.currentThread().getId(), threadLocalDemo.get()));
    }
}

控制臺:

threadId=853 threadLocal value=3
threadId=848 threadLocal value=9
threadId=808 threadLocal value=9
threadId=121 threadLocal value=8
------------------------------------------------
threadId=1 threadLocal value=0 mod=999

Process finished with exit code 0

mod=999 是可見性問題造成,而value值都不一樣則說明不同線程的變量值不一樣。

Spring示例

public abstract class TransactionSynchronizationManager {
    private static final Log logger = LogFactory.getLog(TransactionSynchronizationManager.class);
    private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal("Transactional resources");
    private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations = new NamedThreadLocal("Transaction synchronizations");
    private static final ThreadLocal<String> currentTransactionName = new NamedThreadLocal("Current transaction name");
    private static final ThreadLocal<Boolean> currentTransactionReadOnly = new NamedThreadLocal("Current transaction read-only status");
    private static final ThreadLocal<Integer> currentTransactionIsolationLevel = new NamedThreadLocal("Current transaction isolation level");
    private static final ThreadLocal<Boolean> actualTransactionActive = new NamedThreadLocal("Actual transaction active");
...
}
public final class DateTimeContextHolder {
	private static final ThreadLocal<DateTimeContext> dateTimeContextHolder = new NamedThreadLocal<>("DateTimeContext");
       ...
}

到此,關(guān)于“java中ThreadLocal有什么作用”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

當(dāng)前名稱:java中ThreadLocal有什么作用
文章地址:http://jinyejixie.com/article18/jjhhdp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站排名、建站公司、品牌網(wǎng)站建設(shè)、App設(shè)計、網(wǎng)站設(shè)計公司、網(wǎng)站營銷

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

綿陽服務(wù)器托管
榆中县| 乌海市| 邻水| 石渠县| 信阳市| 武平县| 乌鲁木齐市| 德惠市| 清水河县| 日土县| 巴林左旗| 江口县| 柘城县| 叙永县| 拜泉县| 紫阳县| 来安县| 平江县| 宿迁市| 马公市| 瓦房店市| 清新县| 宜宾市| 福安市| 德化县| 夹江县| 新竹县| 长治市| 云霄县| 化德县| 外汇| 上蔡县| 合山市| 澄迈县| 扎兰屯市| 寿宁县| 浮山县| 清流县| 泽普县| 大姚县| 随州市|