亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java中ThreadLocal的使用

 更新時(shí)間:2023年09月26日 09:02:06   作者:手寫情書  
這篇文章主要介紹了Java中ThreadLocal的使用,靜態(tài)內(nèi)部類的加載是在程序中調(diào)用靜態(tài)內(nèi)部類的時(shí)候加載的,和外部類的加載沒有必然關(guān)系, 但是在加載靜態(tài)內(nèi)部類的時(shí)候 發(fā)現(xiàn)外部類還沒有加載,那么就會(huì)先加載外部類 ,加載完外部類之后,再加載靜態(tài)內(nèi)部類,需要的朋友可以參考下

1.預(yù)備知識(shí)-靜態(tài)內(nèi)部類

靜態(tài)內(nèi)部類的加載時(shí)機(jī)?他和外部類的加載有沒有什么關(guān)系

靜態(tài)內(nèi)部類的加載是在程序中調(diào)用靜態(tài)內(nèi)部類的時(shí)候加載的,和外部類的加載沒有必然關(guān)系, 但是在加載靜態(tài)內(nèi)部類的時(shí)候 發(fā)現(xiàn)外部類還沒有加載,那么就會(huì)先加載外部類 ,加載完外部類之后,再加載靜態(tài)內(nèi)部類.(初始化靜態(tài)變量和靜態(tài)代碼塊etc),如果在程序中單純的使用 外部類,并不會(huì)觸發(fā)靜態(tài)內(nèi)部類的加載

擴(kuò)展 :

一個(gè)類內(nèi)部有靜態(tài)內(nèi)部類和非靜態(tài)內(nèi)部類 , 靜態(tài)內(nèi)部類和非靜態(tài)內(nèi)部類一樣,都是在被調(diào)用時(shí)才會(huì)被加載 ,不過在加載靜態(tài)內(nèi)部類的過程中如果沒有加載外部類,也會(huì)加載外部類,靜態(tài)變量,靜態(tài)方法,靜態(tài)塊等都是類級(jí)別的屬性,而不是單純的對(duì)象屬性.他們?cè)陬惖谝淮伪皇褂脮r(shí)被加載 (記住,是一次使用,不一定是實(shí)例化),我們可以簡單得用 類名.變量 或者 類名.方法來調(diào)用它們, 與調(diào)用沒有被static 修飾過變量和方法不同的是:一般變量和方法是用當(dāng)前對(duì)象的引用(即this)來調(diào)用的, 靜態(tài)的方法和變量則不需要.從一個(gè)角度上來說,它們是共享給所有對(duì)象的,不是一個(gè)角度私有. 這點(diǎn)上,靜態(tài)內(nèi)部類也是一樣的

類的加載時(shí)機(jī):(暫時(shí)的認(rèn)知里是四種) new 一個(gè)類的時(shí)候,調(diào)用類內(nèi)部的 靜態(tài)變量,調(diào)用類的靜態(tài)方法,調(diào)用類的 靜態(tài)內(nèi)部類

public class OuterClass {
    public static String OUTER_DATE = "外部類靜態(tài)變量加載時(shí)間 "+System.currentTimeMillis();
    static {
        System.out.println("外部類靜態(tài)塊加載時(shí)間:" + System.currentTimeMillis());
    }
    public OuterClass() {
        System.out.println("外部類構(gòu)造函數(shù)時(shí)間:" + System.currentTimeMillis());
    }
    static class InnerStaticClass{
        public static String INNER_STATIC_DATE = "靜態(tài)內(nèi)部類靜態(tài)變量加載時(shí)間 "+System.currentTimeMillis();
        private String name;
        static {
            System.out.println("靜態(tài)內(nèi)部類靜態(tài)代碼塊加載時(shí)間:" + System.currentTimeMillis());
        }
    }
    class InnerClass {
        public String INNER_DATE = "";
        public InnerClass() {
            INNER_DATE = "非靜態(tài)內(nèi)部類構(gòu)造器加載時(shí)間"+System.currentTimeMillis();
        }
    }
    public static void main(String[] args) {
        OuterClass outer = new OuterClass();
        System.out.println("非靜態(tài)內(nèi)部類加載時(shí)間: "+outer.new InnerClass().INNER_DATE);
        /**
         * 內(nèi)部靜態(tài)類可以直接用,不需要new
         * 靜態(tài)內(nèi)部類的加載是代碼中需要靜態(tài)內(nèi)部類的時(shí)候才加載,而不是和外部類一起加載的
         * 加載靜態(tài)內(nèi)部類之前,先把外部類的靜態(tài)變量和靜態(tài)代碼塊先執(zhí)行完
         * 執(zhí)行完外部類的代碼后,再執(zhí)行靜態(tài)內(nèi)部類的 靜態(tài)變量和靜態(tài)代碼塊
         * 靜態(tài)內(nèi)部類的 靜態(tài)變量和靜態(tài)代碼塊執(zhí)行完后,然后執(zhí)行業(yè)務(wù)代碼
         * new  外部類的時(shí)候 。外部類的靜態(tài)代碼塊和靜態(tài)變量先執(zhí)行,外部類構(gòu)造函數(shù)后執(zhí)行
         */
        System.out.println("靜態(tài)內(nèi)部類加載時(shí)間____:"+InnerStaticClass.INNER_STATIC_DATE);
    }
}
//什么時(shí)候考慮使用靜態(tài)內(nèi)部類?
//A類中需要一個(gè)B類,但是B類只為A類服務(wù),這種情況不需要將B類單獨(dú)剝離,只需要在A內(nèi)部即可
//一個(gè)類的構(gòu)建有非常多參數(shù)或者十分復(fù)雜的一個(gè)對(duì)象的時(shí)候--引申到了建造者模式

2.ThredLocal

2.1ThrealLocal簡介

ThrealLocal叫做現(xiàn)成變量,意思就是ThradLocal中填充的變量屬于當(dāng)前線程,該變量對(duì)其他線程而言是隔離的,也就是說該變量是當(dāng)前線程獨(dú)有的變量.ThradLocal為變量在每個(gè)線程中都創(chuàng)建了副本,那么每個(gè)線程可訪問自己內(nèi)部的副本變量.

  • 因?yàn)槊總€(gè)Thread內(nèi)有自己的實(shí)例副本,且副本只能由當(dāng)前Thread使用.這也是ThreadLocal命名的由來
  • 既然每個(gè)Thred都有自己的實(shí)例副本,且其他Thread不可訪問,那么不存在多線程的共享問題

總的來說,ThredLocal適用于每個(gè)線程需要自己獨(dú)立的實(shí)例且該實(shí)例需要在多個(gè)方法中被使用,也即變量在線程隔離而在方法或類間共享的場(chǎng)景

在這里插入圖片描述

2.2 ThreadLocal與Synchronized的區(qū)別

ThreadLocal其實(shí)是與線程綁定的一個(gè)變量.ThreadLocal和Synchonized都用于解決多線程并發(fā)訪問

但是ThreadLocal與synchronized有本質(zhì)的區(qū)別 :

  • Synchronized用于線程間的數(shù)據(jù)共享,而ThreadLocal則用于線程間的數(shù)據(jù)隔離
  • Synchronized是利用鎖的機(jī)制,使變量或代碼塊在某一時(shí)刻只能被一個(gè)線程訪問.而ThredLocal為每一個(gè)線程提供了變量的副本,使得每個(gè)線程某一個(gè)時(shí)間訪問到的不是同一個(gè)對(duì)象,這樣就隔離了多個(gè)現(xiàn)線程歲數(shù)據(jù)的共享, 而Synchronized卻正好相反,它用于在多個(gè)線程間通信時(shí)能夠獲得數(shù)據(jù)共享

一句話理解ThreadLocal,向ThreadLocal里面存東西就是向它里面的Map存東西的,然后ThreadLocal把這個(gè)Map掛到當(dāng)前的線程底下,這樣Map就只屬于這個(gè)線程了

2.3 ThreadLocal的簡單使用

public class ThreadLocaDemo {
    private static ThreadLocal<String> localVar = new ThreadLocal<String>();
    static void print(String str) {
        //打印當(dāng)前線程中本地內(nèi)存中本地變量的值
        System.out.println(str + " :" + localVar.get());
        //清除本地內(nèi)存中的本地變量
        localVar.remove();
    }
    public static void main(String[] args) throws InterruptedException {
        new Thread(new Runnable() {
            public void run() {
                ThreadLocaDemo.localVar.set("local_A");
                print("A");
                //打印本地變量
                System.out.println("after remove : " + localVar.get());
            }
        },"A").start();
        Thread.sleep(1000);
        new Thread(new Runnable() {
            public void run() {
                ThreadLocaDemo.localVar.set("local_B");
                print("B");
                System.out.println("after remove : " + localVar.get());
            }
        },"B").start();
    }
}

2.4ThrealLocal的內(nèi)存泄漏問題

 public void set(T value) {
        //1、獲取當(dāng)前線程
        Thread t = Thread.currentThread();
        //2、獲取線程中的屬性 threadLocalMap ,如果threadLocalMap 不為空,
        //則直接更新要保存的變量值,否則創(chuàng)建threadLocalMap,并賦值
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            // 初始化thradLocalMap 并賦值
            createMap(t, value);
    }
  static class ThreadLocalMap {
        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
    }

可看出ThreadLocalMap是ThreadLocal的內(nèi)部靜態(tài)類,而它的構(gòu)成主要是用Entry來保存數(shù)據(jù) ,而且還是繼承的弱引用.在Entry內(nèi)部使用ThreadLocal作為key,使用我們?cè)O(shè)置的value作為value.

//這個(gè)是threadlocal 的內(nèi)部方法
void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    //ThreadLocalMap 構(gòu)造方法
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }
    public T get() {
        //1、獲取當(dāng)前線程
        Thread t = Thread.currentThread();
        //2、獲取當(dāng)前線程的ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        //3、如果map數(shù)據(jù)為空,
        if (map != null) {
            //3.1、獲取threalLocalMap中存儲(chǔ)的值
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        //如果是數(shù)據(jù)為null,則初始化,初始化的結(jié)果,TheralLocalMap中存放key值為threadLocal,值為null
        return setInitialValue();
    }
private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
 public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

remove方法,直接將ThrealLocal 對(duì)應(yīng)的值從當(dāng)前相差Thread中的ThreadLocalMap中刪除.為什么要?jiǎng)h除,這涉及到內(nèi)存泄露的問

實(shí)際上 ThreadLocalMap 中使用的 key 為 ThreadLocal 的弱引用,弱引用的特點(diǎn)是,如果這個(gè)對(duì)象只存在弱引用,那么在下一次垃圾回收的時(shí)候必然會(huì)被清理掉

所以如果 ThreadLocal 沒有被外部強(qiáng)引用的情況下,在垃圾回收的時(shí)候會(huì)被清理掉的,這樣一來 ThreadLocalMap中使用這個(gè) ThreadLocal 的 key 也會(huì)被清理掉。但是,value 是強(qiáng)引用,不會(huì)被清理,這樣一來就會(huì)出現(xiàn) key 為 null 的 value

ThreadLocal其實(shí)是與線程綁定的一個(gè)變量,如此就會(huì)出現(xiàn)一個(gè)問題:如果沒有將ThreadLocal內(nèi)的變量刪除(remove)或替換,它的生命周期將會(huì)與線程共存。通常線程池中對(duì)線程管理都是采用線程復(fù)用的方法,在線程池中線程很難結(jié)束甚至于永遠(yuǎn)不會(huì)結(jié)束,這將意味著線程持續(xù)的時(shí)間將不可預(yù)測(cè),甚至與JVM的生命周期一致。舉個(gè)例字,如果ThreadLocal中直接或間接包裝了集合類或復(fù)雜對(duì)象,每次在同一個(gè)ThreadLocal中取出對(duì)象后,再對(duì)內(nèi)容做操作,那么內(nèi)部的集合類和復(fù)雜對(duì)象所占用的空間可能會(huì)開始持續(xù)膨脹

在這里插入圖片描述

在這里插入圖片描述

我從這個(gè)圖中我們可以非常直觀的看出,ThreadLocalMap其實(shí)是Thread線程的一個(gè)屬性值,而ThreadLocal是維護(hù)ThreadLocalMap這個(gè)屬性指的一個(gè)工具類。

Thread線程可以擁有多個(gè)ThreadLocal維護(hù)的自己線程獨(dú)享的共享變量(這個(gè)共享變量只是針對(duì)自己線程里面共享)

到此這篇關(guān)于Java中ThreadLocal的使用的文章就介紹到這了,更多相關(guān)ThreadLocal的使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java之操作Redis案例講解

    Java之操作Redis案例講解

    這篇文章主要介紹了Java之操作Redis案例講解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • 詳解Java的位操作符

    詳解Java的位操作符

    位操作就是對(duì)這些數(shù)據(jù)進(jìn)行基本的操作。如果基本類型是char、byte或者short類型的數(shù)值進(jìn)行移位處理,那么會(huì)轉(zhuǎn)化成int類型,再進(jìn)行移位的處理
    2017-09-09
  • Spring6當(dāng)中獲取Bean的四種方式小結(jié)

    Spring6當(dāng)中獲取Bean的四種方式小結(jié)

    Spring 為Bean 的獲取提供了多種方式,通常包括4種方式,(也就是說在Spring中為Bean對(duì)象的創(chuàng)建準(zhǔn)備了多種方案,目的是:更加靈活),本文將通過代碼示例詳細(xì)的給大家介紹了一下這四種方式,需要的朋友可以參考下
    2024-04-04
  • 詳解SpringMVC的類型轉(zhuǎn)換及驗(yàn)證方法

    詳解SpringMVC的類型轉(zhuǎn)換及驗(yàn)證方法

    在本篇文章里面我們給大家詳細(xì)分析了SpringMVC的類型轉(zhuǎn)換及驗(yàn)證方法的相關(guān)知識(shí),對(duì)此有需要的朋友們學(xué)習(xí)下吧。
    2018-10-10
  • Java 實(shí)現(xiàn)倒計(jì)時(shí)功能(由秒計(jì)算天、小時(shí)、分鐘、秒)

    Java 實(shí)現(xiàn)倒計(jì)時(shí)功能(由秒計(jì)算天、小時(shí)、分鐘、秒)

    最近做項(xiàng)目遇到這樣的需求,天、小時(shí)、分鐘、秒的數(shù)值都是隔開的,服務(wù)器端只返回一個(gè)時(shí)間戳長度,怎么實(shí)現(xiàn)這樣的功能呢?下面小編給大家?guī)砹薐ava 實(shí)現(xiàn)倒計(jì)時(shí)功能的方案,需要的朋友參考下吧
    2018-01-01
  • Java使用WatchService監(jiān)控文件內(nèi)容變化的示例

    Java使用WatchService監(jiān)控文件內(nèi)容變化的示例

    本篇文章主要介紹了Java使用WatchService監(jiān)控文件變化的示例,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2017-10-10
  • 詳解Java5、Java6、Java7的新特性

    詳解Java5、Java6、Java7的新特性

    本編文章詳細(xì)介紹了Java5、Java6、Java7的新特性,需要的朋友可以參考下
    2017-04-04
  • Spring Cloud Gateway 獲取請(qǐng)求體(Request Body)的多種方法

    Spring Cloud Gateway 獲取請(qǐng)求體(Request Body)的多種方法

    這篇文章主要介紹了Spring Cloud Gateway 獲取請(qǐng)求體(Request Body)的多種方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • SpringMVC中的@RequestMapping注解解析

    SpringMVC中的@RequestMapping注解解析

    這篇文章主要介紹了SpringMVC中的@RequestMapping注解解析,SpringMVC使用@RequestMapping注解為控制器指定可以處理哪些?URL?請(qǐng)求,在控制器的類定義及方法定義處都可標(biāo)注@RequestMapping,需要的朋友可以參考下
    2023-12-12
  • 簡單注解實(shí)現(xiàn)集群同步鎖(spring+redis+注解)

    簡單注解實(shí)現(xiàn)集群同步鎖(spring+redis+注解)

    本文主要介紹了簡單注解實(shí)現(xiàn)集群同步鎖的步驟與方法。具有一定的參考價(jià)值,下面跟著小編一起來看下吧
    2017-01-01

最新評(píng)論