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

Java中ThreadLocal的用法和原理詳解

 更新時間:2023年04月12日 14:07:43   作者:twilight0402  
這篇文章主要為大家詳細介紹了Java中ThreadLocal的用法和原理,文中的示例代碼講解詳細,具有一定的學習價值,感興趣的可以了解一下

用法

  • 隔離各個線程間的數(shù)據(jù)
  • 避免線程內(nèi)每個方法都進行傳參,線程內(nèi)的所有方法都可以直接獲取到ThreadLocal中管理的對象。
package com.example.test1.service;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;

@Component
public class AsyncTest {

    // 使用threadlocal管理
    private static final ThreadLocal<SimpleDateFormat> dateFormatLocal =
            ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));

    // 不用threadlocal進行管理,用于對比
    SimpleDateFormat dateFormat = new SimpleDateFormat();

    // 線程名稱以task開頭
    @Async("taskExecutor")
    public void formatDateSync(String format, Date date) throws InterruptedException {
        SimpleDateFormat simpleDateFormat = dateFormatLocal.get();
        simpleDateFormat.applyPattern(format);
        
        // 所有方法都可以直接使用這個變量,而不用根據(jù)形參傳入
        doSomething();
        
        Thread.sleep(1000);
        System.out.println("sync " + Thread.currentThread().getName() +  " | " + simpleDateFormat.format(date));
        
        // 線程執(zhí)行完畢,清除數(shù)據(jù)
        dateFormatLocal.remove();
    }

    // 線程名稱以task2開頭
    @Async("taskExecutor2")
    public void formatDate(String format, Date date) throws InterruptedException {
        dateFormat.applyPattern(format);
        Thread.sleep(1000);
        System.out.println("normal " + Thread.currentThread().getName() +  " | " + dateFormat.format(date));
    }
}

使用junit進行測試:

	@Test
	void test2() throws InterruptedException {
		for(int index = 1; index <= 10; ++index){
			String format = index + "-yyyy-MM-dd";
			Date time = new Date();
			asyncTest.formatDate(format, time);
		}

		for(int index = 1; index <= 10; ++index){
			String format = index + "-yyyy-MM-dd";
			Date time = new Date();
			asyncTest.formatDateSync(format, time);
		}
	}

結果如下,可以看到?jīng)]有被 ThreadLocal 管理的變量已經(jīng)無法匹配正確的format。

sync task--10 | 10-2023-04-11
sync task--9 | 9-2023-04-11
normal task2-3 | 2-2023-04-11
normal task2-5 | 2-2023-04-11
normal task2-10 | 2-2023-04-11
normal task2-6 | 2-2023-04-11
sync task--1 | 1-2023-04-11
normal task2-7 | 2-2023-04-11
normal task2-8 | 2-2023-04-11
normal task2-9 | 2-2023-04-11
sync task--6 | 6-2023-04-11
sync task--3 | 3-2023-04-11
sync task--2 | 2-2023-04-11
sync task--7 | 7-2023-04-11
sync task--4 | 4-2023-04-11
sync task--8 | 8-2023-04-11
normal task2-4 | 2-2023-04-11
normal task2-1 | 2-2023-04-11
sync task--5 | 5-2023-04-11
normal task2-2 | 2-2023-04-11

實現(xiàn)原理

ThreadLocal中獲取數(shù)據(jù)的過程:

先獲取對應的線程。

通過 getMap(t)拿到線程中的 ThreadLocalMap

ThreadLocalMap 是一個重新實現(xiàn)的散列表,基于兩個元素實現(xiàn)散列:

  • 用戶定義的ThreadLocal對象,例如:dateFormatLocal
  • 封裝了valueEntry對象。

通過map.getEntry(this)方法,根據(jù)當前的 threadlocal對象在散列表中獲得對應的Entry

如果是第一次使用get(), 則使用 setInitialValue()調(diào)用用戶重寫的initialValue()方法創(chuàng)建map并使用用戶指定的值初始化。

在這種設計方式下,線程死去的時候,線程共享變量ThreadLocalMap會被銷毀。

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();
}

注意 Entry對象是弱引用:

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

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

弱引用的常見用法是:

WeakReference<RoleDTO> weakReference = new WeakReference<>(new RoleDTO());

因此,在Entry中,k 代表ThreadLocal對象,它是弱引用。v代表ThreadLocal管理的那個value,是強引用。

內(nèi)存泄漏

內(nèi)存泄漏是指無用對象(不再使用的對象)持續(xù)占有內(nèi)存或無用對象的內(nèi)存得不到及時釋放,從而造成內(nèi)存空間的浪費稱為內(nèi)存泄漏。隨著垃圾回收器活動的增加以及內(nèi)存占用的不斷增加,程序性能會逐漸表現(xiàn)出來下降,極端情況下,會引發(fā)OutOfMemoryError導致程序崩潰。

內(nèi)存泄漏問題主要在線程池中出現(xiàn),因為線程池中的線程是不斷執(zhí)行的,從任務隊列中不斷獲取新的任務執(zhí)行。但是任務中可能有ThreadLocal對象,這些對象的ThreadLocal會保存在線程的ThreadLocalMap中,因此ThreadLocalMap會越來越大。

但是ThreadLocal是由任務(worker)傳入的,一個任務執(zhí)行結束后,對應的ThreadLocal對象會被銷毀。線程中的關系是: Thread -> ThreadLoalMap -> Entry<ThreadLocal, Object>。ThreadLocal由于是弱引用會,在GC的時候會被銷毀,這會導致 ThreadLoalMap中存在Entry<null, Object>。

使用remove()

由于線程池中的線程一直在運行,如果不對ThreadLoalMap進行清理,那Entry<null, Object>會一直占用內(nèi)存。remove()方法會清除key==nullEntry

使用static修飾

ThreadLocal設置成static可以避免一個線程類多次傳入線程池后重復創(chuàng)建Entry。例如,有一個用戶定義的線程

public class Test implements Runnable{
    private static ThreadLocal<Integer> local = new ThreadLocal<>();
    @Override
    public void run() {
        // do something
    }
}

使用線程池處理10個任務。那么線程池中每個用來處理任務的線程的Thread.ThreadLocalMap中都會保存一個Entry<local, Integer>,由于添加了static關鍵字,所有每個線程中的Entry中的local變量引用的都是同一個變量。這時就算發(fā)生內(nèi)存泄漏,所有的Test類也只有一個local對象,不會導致內(nèi)存占用過多。

@Test
void contextLoads() {
   Runnable runnable = () -> {
      System.out.println(Thread.currentThread().getName());
   };

   for(int index = 1; index <= 10; ++index){
      taskExecutor2.submit(new com.example.test1.service.Test());
   }
}

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

相關文章

  • Java判斷本機IP地址類型的方法

    Java判斷本機IP地址類型的方法

    Java判斷本機IP地址類型的方法,需要的朋友可以參考一下
    2013-03-03
  • Java中HashTable和HashMap的區(qū)別_動力節(jié)點Java學院整理

    Java中HashTable和HashMap的區(qū)別_動力節(jié)點Java學院整理

    HashTable和HashMap主要的區(qū)別有:線程安全性,同步(synchronization),以及速度。接下來通過本文給大家簡單介紹下HashTable和HashMap的區(qū)別,需要的的朋友參考下吧
    2017-04-04
  • SpringBoot中FailureAnalyzer的使用詳解

    SpringBoot中FailureAnalyzer的使用詳解

    這篇文章主要介紹了SpringBoot中FailureAnalyzer的使用詳解,FailureAnalyzer攔截啟動時異常,將異常轉換成更加易讀的信息并包裝成org.springframework.boot.diagnostics.FailureAnalysis對象,監(jiān)控應用啟動過程,需要的朋友可以參考下
    2023-12-12
  • mybatis 批量將list數(shù)據(jù)插入到數(shù)據(jù)庫的實現(xiàn)

    mybatis 批量將list數(shù)據(jù)插入到數(shù)據(jù)庫的實現(xiàn)

    這篇文章主要介紹了mybatis 批量將list數(shù)據(jù)插入到數(shù)據(jù)庫的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-07-07
  • SpringBoot整合Java DL4J實現(xiàn)文本分類系統(tǒng)

    SpringBoot整合Java DL4J實現(xiàn)文本分類系統(tǒng)

    在當今信息爆炸的時代,自然語言處理領域中的文本分類顯得尤為重要,文本分類能夠高效地組織和管理海量的文本數(shù)據(jù),隨著互聯(lián)網(wǎng)的飛速發(fā)展,我們每天都被大量的文本信息所包圍,本文將介紹如何使用 Spring Boot 整合 Java Deeplearning4j 來構建一個文本分類系統(tǒng)
    2024-10-10
  • JAVA如何按字節(jié)截取字符串

    JAVA如何按字節(jié)截取字符串

    這篇文章主要介紹了JAVA如何按字節(jié)截取字符串,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-01-01
  • SpringBoot開啟異步調(diào)用方法

    SpringBoot開啟異步調(diào)用方法

    這篇文章主要為大家詳細介紹了SpringBoot開啟異步調(diào)用方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-09-09
  • Springboot啟動原理和自動配置原理解析

    Springboot啟動原理和自動配置原理解析

    這篇文章主要介紹了Springboot啟動原理和自動配置原理解析,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-04-04
  • Java生成二維碼的實例代碼

    Java生成二維碼的實例代碼

    這篇文章主要介紹了Java生成二維碼的實例代碼,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2020-09-09
  • Java Annotation Overview詳解

    Java Annotation Overview詳解

    這篇文章主要介紹了Java Annotation Overview,需要的朋友可以參考下
    2014-02-02

最新評論

安顺市| 黎川县| 富顺县| 太仓市| 罗城| 名山县| 乌拉特中旗| 麦盖提县| 大悟县| 都兰县| 溧水县| 长葛市| 琼中| 昆明市| 仪陇县| 攀枝花市| 五峰| 宜宾县| 漳平市| 高州市| 万宁市| 仲巴县| 吉首市| 枝江市| 邵武市| 京山县| 马鞍山市| 临安市| 高平市| 萝北县| 麦盖提县| 梁平县| 保德县| 沁阳市| 新河县| 禹城市| 精河县| 安化县| 敦煌市| 德兴市| 彭山县|