日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ThreadLocal基本使用和内存泄漏分析

發布時間:2025/1/21 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ThreadLocal基本使用和内存泄漏分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

ThreadLocal基礎部分

ThreadLoal的作用

保存線程的獨立變量,即每個線程維護一份。這種變量在線程的生命周期內起作用,減少同一個線程內多個函數之間公共變量傳遞麻煩。

使用場景

需要給不同的線程保存不同的信息時。

基礎使用

public class TestThreadLocal {private static ThreadLocal<Integer> threadLocal=new ThreadLocal<Integer>(); // private static ThreadLocal<Integer> threadLocal=new ThreadLocal<Integer>(){ // // @Override // protected Integer initialValue() { // return 0; // } // };public static void main(String[] args) {Thread t1=new Thread(new Runnable() {@Overridepublic void run() {System.out.println(threadLocal.get());}});Thread t2=new Thread(new Runnable() {@Overridepublic void run() {threadLocal.set(3);System.out.println("t2:"+threadLocal.get());}});t1.start();t2.start();System.out.println(threadLocal.get());} }

如果需要設置默認值的話,可以實現initialValue方法。

典型場景1:我們知道SimpleDateFormat的對象如果多線程使用的話會有線程不安全的問題。具體代碼如下:

public class TestThreadLocal {public static ExecutorService executorService = Executors.newFixedThreadPool(16);private static SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");public static void main(String[] args) throws InterruptedException {for (int i=0;i<1000;i++){executorService.submit(new Runnable() {@Overridepublic void run() {String format = simpleDateFormat.format(new Date());try {Date parse = simpleDateFormat.parse("2021-09-01 00:00:00");} catch (ParseException e) {e.printStackTrace();}System.out.println(format);}});}Thread.sleep(3000);executorService.shutdownNow();} }

運行結果如下:

可以看出,發生了異常。

方法1:我們可以改為每次都new一個新的SimpleDateFormat對象的話,這樣再運行是沒問題的。但是有些資源浪費。

方法2:使用ThreadLocal來解決。假設線程池里共16個線程,那我們總共16個SimpleDateFormat對象就可以應付所有的日期格式化的調用。

代碼如下:

public class TestThreadLocal {public static ExecutorService executorService = Executors.newFixedThreadPool(16);private static ThreadLocal<SimpleDateFormat> threadLocal=new ThreadLocal<SimpleDateFormat>(){@Overrideprotected SimpleDateFormat initialValue() {return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");}};private static SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");public static void main(String[] args) throws InterruptedException {for (int i=0;i<1000;i++){executorService.submit(new Runnable() {@Overridepublic void run() {String format = threadLocal.get().format(new Date());try {Date parse = threadLocal.get().parse("2021-09-01 00:00:00");} catch (ParseException e) {e.printStackTrace();}System.out.println(format);}});}Thread.sleep(3000);executorService.shutdownNow();} }

注意:?如果不使用線程池,線程結束,線程里的threadLocalMap也會被回收。但是如果使用線程池,線程池里面的線程會被復用,線程里的threadLocalMap不會被回收,就造成了內存泄漏。按照正確的使用方法應該是每次用完了remove,但是這樣效率就很低。還不如方法1每次去new一個新的SimpleDateFormat對象。(但個人覺得其實還好,泄漏一點也沒關系,不過threadlocal畢竟不是專門解決線程安全問題的,不推薦這么用)

正確使用方法

  • 每次使用完ThreadLocal都調用它的remove()方法清除數據
  • 將ThreadLocal變量定義成private static,這樣就一直存在ThreadLocal的強引用,也就能保證任何時候都能通過ThreadLocal的弱引用訪問到Entry的value值,進而清除掉 。

ThreadLocal 高級部分

ThreadLocal為什么會內存泄露?

內存泄漏(Memory Leak)是指程序中已動態分配的堆內存由于某種原因程序未釋放或無法釋放,造成系統內存的浪費,導致程序運行速度減慢甚至系統崩潰等嚴重后果。

試想:

一個線程對應一塊工作內存,線程可以存儲多個ThreadLocal。那么假設,開啟1萬個線程,每個線程創建1萬個ThreadLocal,也就是每個線程維護1萬個ThreadLocal小內存空間,而且當線程執行結束以后,假設這些ThreadLocal里的Entry還不會被回收,那么將很容易導致堆內存溢出。

怎么辦?難道JVM就沒有提供什么解決方案嗎?

答案:

  • JVM利用設置ThreadLocalMap的Key為弱引用,來避免內存泄露。
  • JVM利用調用remove、get、set方法的時候,順道回收臟value值。
  • ThreadLocal的關系圖如下所示:

    Thread里面維護了一個ThreadLocalMap,這個map里面的key是弱引用的readLocal實例。value是我們設置進去的值。當把treadLocal實例對象置為null后,沒有任何強引用指向threadLocal實例,所以theadLocal將會被gc回收。但是我們的value不會被回收,因為存在一個thread連接過來的強引用。只有當thread結束后,強引用斷開,map、value等將全部被回收。

    如下圖:

    但是很多時候我們使用線程池,為了復用線程,thread生命周期沒有結束,所以無法回收,造成內存泄漏。


    書山有路勤為徑,學海無涯苦作舟

    總結

    以上是生活随笔為你收集整理的ThreadLocal基本使用和内存泄漏分析的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。