最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中ThreadLocal避免內存泄漏的方法詳解

 更新時間:2023年05月19日 08:24:45   作者:越走越遠的風  
ThreadLocal是Java中的一個線程本地存儲機制,它允許每個線程擁有一個獨立的本地存儲空間,用于存儲該線程的變量,本文主要介紹了ThreadLocal如何避免內存泄漏,需要的朋友可以參考下

ThreadLocal簡介

ThreadLocal 是 Java 中的一個線程本地存儲機制,它允許每個線程擁有一個獨立的本地存儲空間,用于存儲該線程的變量。ThreadLocal 提供了一種簡單的方式來解決多線程環(huán)境下共享變量的問題,避免了在多線程環(huán)境下出現(xiàn)的線程安全問題。

ThreadLocal簡單用法

public class ThreadLocalDemo {  
    private static final ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);  
    
    //當前值+1
    public static void increment() {  
        int value = threadLocal.get();  
        threadLocal.set(value + 1);  
    }  

    public static void main(String[] args) throws InterruptedException {  
        for (int i = 0; i < 10; i++) {  
            new Thread(() -> {  
                String threadName = Thread.currentThread().getName();  
                increment();  
                System.out.println(threadName + " 當前threadLocal的值為:" + threadLocal.get());  
            }).start();  
        }  
    }  
  
}

輸出結果為

Thread-0 當前threadLocal的值為:1
Thread-5 當前threadLocal的值為:1
Thread-3 當前threadLocal的值為:1
Thread-4 當前threadLocal的值為:1
Thread-6 當前threadLocal的值為:1
Thread-2 當前threadLocal的值為:1
Thread-9 當前threadLocal的值為:1
Thread-1 當前threadLocal的值為:1
Thread-7 當前threadLocal的值為:1
Thread-8 當前threadLocal的值為:1

我們發(fā)現(xiàn)每個線程雖然都共享同一個threadLocal實例,但它們并沒有發(fā)生相互干擾的情況,而是各自產(chǎn)生獨立的值,這是因為我們通過ThreadLocal為每一個線程提供了單獨的副本。

使用場景

spring事務模板類

//使用示例
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

//查看源碼
public static TransactionStatus currentTransactionStatus() throws NoTransactionException {  
    TransactionInfo info = currentTransactionInfo();  
    if (info == null || info.transactionStatus == null) {  
        throw new NoTransactionException("No transaction aspect-managed TransactionStatus in scope");  
    }  
    return info.transactionStatus;  
}

//currentTransactionInfo方法
@Nullable  
protected static TransactionInfo currentTransactionInfo() throws NoTransactionException {  
    return transactionInfoHolder.get();  
}

//這里使用了ThreadLocal
private static final ThreadLocal<TransactionInfo> transactionInfoHolder =  
new NamedThreadLocal<>("Current aspect-driven transaction");

HttpServletRequest

項目中要獲取當前HttpServletRequest可以使用

HttpServletRequest request =  
((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();

我們來看看RequestContextHolder.getRequestAttributes()方法

@Nullable  
public static RequestAttributes getRequestAttributes() {  
    RequestAttributes attributes = requestAttributesHolder.get();  
    if (attributes == null) {  
        attributes = inheritableRequestAttributesHolder.get();  
    }  
    return attributes;  
}

private static final ThreadLocal<RequestAttributes> requestAttributesHolder =  
new NamedThreadLocal<>("Request attributes");  
  
private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =  
new NamedInheritableThreadLocal<>("Request context");

aop調用鏈傳遞

LCN/seata都是使用ThreadLocal傳遞調用鏈的,這里就不展開講了。

內存泄漏與內存溢出

內存泄漏

內存泄漏指的是程序中存在某些對象或資源沒有被妥善地釋放,導致這些對象或資源一直占用著內存,而無法被回收。隨著時間的推移,這些未釋放的對象或資源會越來越多,最終耗盡系統(tǒng)的內存資源,導致系統(tǒng)崩潰。

常見的內存泄漏包括:

  • 對象被創(chuàng)建后,沒有及時被銷毀,成為垃圾對象。
  • 沒有正確關閉IO資源。
  • 緩存沒有被清空。
  • 靜態(tài)集合類對象未刪除引用。
  • 單例模式下對象未及時釋放等。

內存溢出

內存溢出指的是程序在申請內存時,無法獲得足夠的內存空間,導致程序無法正常運行。通常情況下,當程序需要使用的內存超過了系統(tǒng)能夠提供的內存時,就會發(fā)生內存溢出。

常見的內存溢出包括:

  • 堆內存溢出:由于創(chuàng)建了過多的對象或者某些對象太大,導致堆內存不足。
  • 棧內存溢出:由于方法調用過多或者某些方法的遞歸調用層數(shù)過多,導致棧內存不足。
  • 永久代內存溢出:由于創(chuàng)建了過多的類或者字符串,導致永久代內存不足。

區(qū)別

內存泄漏和內存溢出的區(qū)別在于它們發(fā)生的原因和表現(xiàn)形式。內存泄漏是指對象或者資源無法被妥善釋放,導致系統(tǒng)資源浪費,而內存溢出則是指系統(tǒng)不能分配所需內存,導致程序崩潰或者異常。通常情況下,內存泄漏會逐漸消耗系統(tǒng)資源,而內存溢出則是突然發(fā)生的。

解決內存泄漏的方法是找到未被正確釋放的對象或資源,并手動進行釋放。而解決內存溢出的方法則需要優(yōu)化程序代碼,減少內存使用量,或增加系統(tǒng)內存大小等方式來解決。

java強軟弱虛

Java中的引用類型有四種:強引用(Strong Reference)、軟引用(Soft Reference)、弱引用(Weak Reference)和虛引用(Phantom Reference)。它們之間的主要區(qū)別在于對象被垃圾回收時的行為不同。

強引用

強引用是默認類型的引用,當我們通過“new”關鍵字創(chuàng)建一個對象時,該對象會被分配到堆內存中,并且默認情況下,該對象的引用是強引用。只要強引用存在,垃圾回收器就不會將其回收。

例如:

Object obj = new Object();

在上面的代碼中,obj是一個強引用,因此只有當obj變量被顯示地設置為null時,才能使對象成為垃圾,等待垃圾回收器收集。

軟引用

軟引用可以讓對象存活更長時間,直到內存不足時才回收它。如果垃圾回收器需要更多的內存,則會回收只被軟引用引用的對象。當一個對象只被軟引用引用時,它會被保留在內存中,直到系統(tǒng)內存不夠用或者垃圾回收器需要更多空間為止。通過軟引用可以實現(xiàn)一些緩存功能。

例如:

SoftReference<Object> softRef = new SoftReference<>(new Object());

在上面的代碼中,softRef是一個軟引用,當垃圾回收器需要內存時,它可以將該對象回收,并釋放所占用的內存。

弱引用

弱引用比軟引用生命期更短,當一個對象只被弱引用引用時,當垃圾回收器運行時,不管當前內存是否充足,都會將其回收。弱引用通常用于實現(xiàn)緩存機制或者觀察者模式。

例如:

WeakReference<Object> weakRef = new WeakReference<>(new Object());

在上面的代碼中,weakRef是一個弱引用,這意味著垃圾回收器可以隨時將該對象回收,而無需考慮系統(tǒng)內存是否充足。

虛引用

虛引用也稱為幽靈引用,與其他三種引用方式不同,它并不會決定對象是否能存活。如果一個對象只有虛引用,那么就像沒有任何引用一樣,它的內存會被回收,但是在回收之前會調用finalize()方法。虛引用主要用于管理DirectBuffer的生命周期。

例如:

PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), null);

在上面的代碼中,phantomRef是一個虛引用,當垃圾回收器發(fā)現(xiàn)該對象的內存已被回收時,它會將其插入隊列中,并在下一次調用垃圾回收器時通知引用對象被回收了。

ThreadLocal原理

ThreadLocal

ThreadLocal是一個泛型類,它提供了get()、set()和remove()方法來獲取、設置和刪除當前線程的變量副本。它的原理是在每個Thread對象中都有一個ThreadLocalMap類型的私有變量threadLocals,該變量存儲著當前線程所對應的所有ThreadLocal變量的值。

public T get() { 
    //獲取當前線程
    Thread t = Thread.currentThread();  
    //獲取當前線程的ThreadLocalMap變量
    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();  
}

public void set(T value) {  
    Thread t = Thread.currentThread();  
    ThreadLocalMap map = getMap(t);  
    if (map != null)  
        map.set(this, value);  
    else  
        createMap(t, value);  
}

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

ThreadLocalMap

ThreadLocalMap是ThreadLocal的內部類,它實際上就是一個HashMap,用于存儲當前線程所對應的所有ThreadLocal變量的值。每個ThreadLocal對象都會被保存在ThreadLocalMap中,并且使用ThreadLocal作為key來訪問它的變量值。這樣做的好處是每個線程都可以獨立維護自己的數(shù)據(jù),而不會與其他線程產(chǎn)生沖突。

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

ThreadLocal.ThreadLocalMap threadLocals = null;

這里需要注意的是ThreadLocalMap每個元素都是Entry,而它是用弱引用對象作為key存儲在ThreadLocalMap中

static class Entry extends WeakReference<ThreadLocal<?>> {  
    /** The value associated with this ThreadLocal. */  
    Object value;  

    Entry(ThreadLocal<?> k, Object v) {  
        super(k);  
        value = v;  
    }  
}

實現(xiàn)原理

當我們通過ThreadLocal類創(chuàng)建一個新的變量時,實際上是在當前線程的threadLocals變量中創(chuàng)建了一個新的Entry對象,該對象的key是ThreadLocal對象本身,value則是我們設置的變量值。這個Entry對象會存儲在ThreadLocalMap中。

當我們需要在當前線程中訪問這個變量時,ThreadLocal會根據(jù)當前線程獲取對應的ThreadLocalMap對象,并根據(jù)ThreadLocal對象作為key來查找該變量的值。由于每個線程都有自己獨立的ThreadLocalMap對象,因此不同線程之間的變量互不干擾。

ThreadLocal內存泄漏原因

每個ThreadLocal對象都會被存儲在當前線程的ThreadLocalMap中,并且使用ThreadLocal對象作為key來訪問它的變量值。由于使用的是弱引用對象作為key,當一個ThreadLocal對象沒有被任何線程引用時,該對象就會被回收。

但是,即使ThreadLocal對象已經(jīng)被回收,對應的變量副本仍然存在于該線程的ThreadLocalMap中。這是因為ThreadLocalMap內部使用了強引用對象(Entry對象)來引用變量副本,只有在當前線程被回收時,ThreadLocalMap中對應的Entry才會被回收。

也就是說,ThreadLocal對象雖然使用的是弱引用,但是與之關聯(lián)的變量副本卻是通過強引用對象間接引用的,因此在ThreadLocal對象被回收后,其變量副本可能不會立刻被回收。如果我們沒有手動調用remove()方法將變量副本從ThreadLocalMap中清除,那么它就會一直存在于內存中,從而導致內存泄漏問題。

ThreadLocal內存泄漏常見場景

ThreadLocal內存泄漏的原因主要是由于線程復用導致的。

線程池

當我們使用線程池時,如果在線程中使用了ThreadLocal變量,那么該變量并不會被自動清除。線程池中的線程是可以被重復利用的,如果我們在一個線程中使用了ThreadLocal變量,并且沒有在該線程結束前手動清除它,那么這個變量將會一直存在于ThreadLocalMap中,即使該線程已經(jīng)被回收,這就會導致內存泄漏。

長時間持有

如果我們在一個線程中創(chuàng)建了ThreadLocal變量,并且一直持有它卻不使用,這也會導致內存泄漏問題。在這種情況下,由于該變量一直存在于ThreadLocalMap中,即使該線程已經(jīng)被回收,該變量也無法被釋放,最終會導致內存泄漏。

ThreadLocal避免內存泄漏方法

為了避免ThreadLocal內存泄漏問題,我們可以采取以下措施:

  • 在使用完ThreadLocal變量后,應該盡快調用remove()方法將其從ThreadLocalMap中清除,以便讓垃圾回收器回收它們。
  • 將ThreadLocal變量定義成private static類型的,并且在使用完之后手動清除,以避免線程重用時引起的內存泄漏問題。
  • 不要在線程池中使用ThreadLocal變量,如果必須使用,應該在使用完后手動清理。

總結

雖然ThreadLocal可以解決線程安全問題,但是在使用完之后需要手動清除,以避免線程重用時引起的內存泄漏問題。

以上就是Java中ThreadLocal避免內存泄漏的方法詳解的詳細內容,更多關于Java ThreadLocal避免內存泄漏的資料請關注腳本之家其它相關文章!

相關文章

  • spring boot如何加入mail郵件支持

    spring boot如何加入mail郵件支持

    這篇文章主要介紹了spring boot如何加入mail郵件支持,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-12-12
  • Java?Lombok實現(xiàn)手機號碼校驗的示例代碼

    Java?Lombok實現(xiàn)手機號碼校驗的示例代碼

    手機號碼校驗通常是系統(tǒng)開發(fā)中最基礎的功能之一,本文主要介紹了Java?Lombok實現(xiàn)手機號碼校驗的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-07-07
  • String StringBuilder StringBuffer區(qū)別以及源碼分析

    String StringBuilder StringBuffer區(qū)別以及源碼分析

    string是C++標準庫的一個重要的部分,主要用于字符串處理??梢允褂幂斎胼敵隽鞣绞街苯舆M行string操作,同時,C++的算法庫對string類也有著很好的支持,并且string類還和c語言的字符串之間有著良好的接口
    2021-06-06
  • Spring Boot Admin監(jiān)控服務如何使用

    Spring Boot Admin監(jiān)控服務如何使用

    這篇文章主要介紹了Spring Boot Admin監(jiān)控服務如何使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-04-04
  • Java中List、Set、Map的區(qū)別和實現(xiàn)方式示例代碼

    Java中List、Set、Map的區(qū)別和實現(xiàn)方式示例代碼

    這篇文章主要介紹了Java中List、Set、Map的區(qū)別和實現(xiàn)方式示例代碼,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-06-06
  • 詳解Spring配置及事務的使用

    詳解Spring配置及事務的使用

    這篇文章主要介紹了詳解Spring配置及事務的使用,文中附含詳細的示例代碼說明,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2021-09-09
  • Java中File、Base64、MultipartFile之間相互轉換的代碼詳解

    Java中File、Base64、MultipartFile之間相互轉換的代碼詳解

    File、Base64和MultipartFile都是在編程中常用的類或者數(shù)據(jù)類型,用于處理文件和數(shù)據(jù)的存儲、傳輸和轉換等操作,本文將給大家介紹了Java中File、Base64、MultipartFile之間相互轉換,文中有詳細的代碼示例供大家參考,需要的朋友可以參考下
    2024-04-04
  • jstack報錯Unable to open socket file解決

    jstack報錯Unable to open socket file解決

    這篇文章主要為大家介紹了jstack報錯Unable to open socket file的解決方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2024-02-02
  • 詳解java接口基礎知識附思維導圖

    詳解java接口基礎知識附思維導圖

    這篇文章主要介紹了java接口基礎知識,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-04-04
  • 詳解Spring Security 中的四種權限控制方式

    詳解Spring Security 中的四種權限控制方式

    這篇文章主要介紹了詳解Spring Security 中的四種權限控制方式,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-10-10

最新評論

无锡市| 扎囊县| 阳原县| 开江县| 蓬溪县| 思茅市| 安化县| 凌云县| 虹口区| 商水县| 成武县| 景洪市| 阿巴嘎旗| 含山县| 巴东县| 康乐县| 南皮县| 深州市| 秦安县| 科技| 绍兴市| 穆棱市| 高安市| 汕头市| 深圳市| 邵武市| 巴林右旗| 定远县| 徐汇区| 搜索| 蓝田县| 华宁县| 喀喇沁旗| 水城县| 包头市| 永胜县| 容城县| 伊川县| 洛川县| 滁州市| 钦州市|