欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Java中ThreadLocal類型及簡單用法

 更新時間:2021年10月13日 16:03:46   作者:Mr.Ymx  
ThreadLocal實例通常是希望將狀態(tài)與線程關聯(lián)起來的類中的私有靜態(tài)字段,下面通過例子給大家詳細介紹Java中ThreadLocal類型及簡單用法,感興趣的朋友跟隨小編一起看看吧

1 基本概念

ThreadLocal類提供了線程局部變量。這些變量與普通變量的不同之處在于,每個訪問一個變量(通過其get或set方法)的線程都有自己的、獨立初始化的變量副本。ThreadLocal實例通常是希望將狀態(tài)與線程關聯(lián)起來的類中的私有靜態(tài)字段(例如,用戶ID或事務ID)。

例如,下面的類生成每個線程本地的唯一標識符。 線程的id在第一次調用ThreadId.get()時被賦值,并且在后續(xù)調用中保持不變。

public class ThreadId {
    // 包含要分配的下一個線程ID的原子整數
    private static final AtomicInteger nextId = new AtomicInteger(0);

    // 包含每個線程ID的線程局部變量
    private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() {
        @Override 
        protected Integer initialValue() {
            return nextId.getAndIncrement();
        }
    };

    // 返回當前線程的唯一ID,并在必要時賦值
    public static int get() {
        return threadId.get();
    }
}

只要線程是活的并且ThreadLocal實例是可訪問的,每個線程都持有一個對線程局部變量副本的隱式引用; 當一個線程離開后,它的所有線程本地實例副本都將被垃圾收集(除非存在對這些副本的其他引用)

2 簡單使用

private static ThreadLocal<String> threadLocal = new ThreadLocal();

private static void print(String thread) {
    //打印當前線程中本地內存中本地變量的值
    System.out.println(thread + " :" + threadLocal.get());
    //清除本地內存中的本地變量
    threadLocal.remove();
}

public static void main(String[] args) {
    new Thread(() -> {
        //設置線程1副本變量的值
        threadLocal.set("I am Thread1");
        //調用打印方法
        print("thread1");
        //打印本地變量
        System.out.println("after remove : " + threadLocal.get());
    }).start();

    new Thread(() -> {
        //設置線程2副本變量的值
        threadLocal.set("I am Thread2");
        //調用打印方法
        print("thread2");
        System.out.println("after remove : " + threadLocal.get());
    }).start();
}

運行結果:

thread1 :I am Thread1
thread2 :I am Thread2
after remove : null
after remove : null

由上邊的程序可以看出,ThreadLocal就是將本地變量在多線程訪問條件下給每個線程一個副本變量,圖示:

在這里插入圖片描述

3 應用場景

最典型應用場景就是Spring的聲明式事務、 解決數據庫連接、Session管理

那數據庫鏈接為例:

將數據庫的鏈接示例在每個線程中都有一份副本數據,在某一個線程關閉連接的時候就不會關閉其他鏈接,session同理。

private static final ThreadLocal<Connection> connection = new ThreadLocal() {
    @Override
    protected Object initialValue() {
        return Connection.getConnection();
    }
};

private static Connection getConnections() {
    return connection.get();
}

public static void main(String[] args) {
    new Thread(() -> {
        Connection connection = getConnections();
        //不會關閉其他鏈接
        connection.close();
    }).start();
    new Thread(() -> {
        Connection connection = getConnections();
        connection.close();
    }).start();
}

4 底層原理

ThreadLocal類型主要有3個方法和一個數據結構,分別是get()、set(Object)、remove()及ThreadLocalMap

4.1 set(Object)

將該線程局部變量的當前線程副本設置為指定的值。 大多數子類都不需要重寫這個方法,只依賴于initialValue方法來設置線程局部變量的值。

參數: Value -要存儲在當前線程本地線程的副本中的值。

public void set(T value) {
    //獲取調用者線程
    Thread t = Thread.currentThread();
    //以當前線程作為key值,去查找對應的線程變量,找到對應的map
    ThreadLocalMap map = getMap(t);
    //如果map不為null,就直接添加本地變量,key為當前定義的ThreadLocal變量的this引用,值為添加的本地變量值
    if (map != null)
        map.set(this, value);
    //如果map為null,說明首次添加,需要首先創(chuàng)建出對應的map
    else
        createMap(t, value);
}

4.2 get()

返回該線程局部變量的當前線程副本中的值。 如果變量在當前線程中沒有值,則首先將其初始化為initialValue方法調用所返回的值。

返回: 這個線程本地的當前線程值

public T get() {
    Thread t = Thread.currentThread();
    //獲取當前線程的threadLocals變量	
    ThreadLocalMap map = getMap(t);
    //如果threadLocals變量不為null,就可以在map中查找到本地變量的值
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    //執(zhí)行到此處,threadLocals為null,調用該更改初始化當前線程的threadLocals變量
    return setInitialValue();
}

private T setInitialValue() {
    //protected T initialValue() {return null;}
    T value = initialValue();
    //獲取當前線程
    Thread t = Thread.currentThread();
    //以當前線程作為key值,去查找對應的線程變量,找到對應的map
    ThreadLocalMap map = getMap(t);
    //如果map不為null,就直接添加本地變量,key為當前線程,值為添加的本地變量值
    if (map != null)
        map.set(this, value);
    //如果map為null,說明首次添加,需要首先創(chuàng)建出對應的map
    else
        createMap(t, value);
    return value;
}

4.3 remove()

移除此線程局部變量的當前線程值。如果這個線程局部變量隨后被當前線程讀取,它的值將通過調用它的initialValue方法重新初始化,除非它的值是由當前線程在中間設置的。這可能導致在當前線程中多次調用initialValue方法。

public void remove() {
    //獲取當前線程綁定的threadLocals
    ThreadLocalMap m = getMap(Thread.currentThread());
    //如果map不為null,就移除當前線程中指定ThreadLocal實例的本地變量
    if (m != null)
        m.remove(this);
}

4.4 ThreadLocalMap

static class ThreadLocalMap {
    static class Entry extends WeakReference<ThreadLocal<?>> {
        Object value;
        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }
    private static final int INITIAL_CAPACITY = 16;
    //有效Entry數組
    private Entry[] table;
    //大小
    private int size = 0;
    //負載因子
    private int threshold; // Default to 0
}

5 內存泄漏隱患和防止策略

5.1 為什么會發(fā)生內存泄漏?

在線程池中線程的存活時間太長,往往都是和程序同生共死的,這樣 Thread 持有的 ThreadLocalMap 一直都不會被回收,再加上 ThreadLocalMap 中的 Entry 對 ThreadLocal 是弱引用(WeakReference),所以只要 ThreadLocal 結束了自己的生命周期是可以被回收掉的。
Entry 中的 Value 是被 Entry 強引用的,即便 value 的生命周期結束了,value 也是無法被回收的,導致內存泄露。

5.2 怎樣防止內存泄漏?

  • ThreadLocal申明為private static final xxx
  • ThreadLocal使用后務必調用remove方法。

參考文章:

https://www.cnblogs.com/fsmly/p/11020641.html

https://blog.csdn.net/meism5/article/details/90413860

https://blog.csdn.net/zzg1229059735/article/details/82715741

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

相關文章

  • Java線程安全解決方案(synchronized,ReentrantLock,Atomic)

    Java線程安全解決方案(synchronized,ReentrantLock,Atomic)

    這篇文章主要介紹了Java線程安全解決方案(synchronized,ReentrantLock,Atomic),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-09-09
  • mybatis?like模糊查詢特殊字符報錯轉義處理方式

    mybatis?like模糊查詢特殊字符報錯轉義處理方式

    這篇文章主要介紹了mybatis?like模糊查詢特殊字符報錯轉義處理方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • Java?spring?boot實現批量刪除功能詳細示例

    Java?spring?boot實現批量刪除功能詳細示例

    這篇文章主要給大家介紹了關于Java?spring?boot實現批量刪除功能的相關資料,文中通過代碼以及圖文將實現的方法介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2023-08-08
  • 分布式醫(yī)療掛號系統(tǒng)EasyExcel導入導出數據字典的使用

    分布式醫(yī)療掛號系統(tǒng)EasyExcel導入導出數據字典的使用

    這篇文章主要為大家介紹了分布式醫(yī)療掛號系統(tǒng)EasyExcel導入導出數據字典的使用,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-04-04
  • SpringBoot整合Mongodb實現增刪查改的方法

    SpringBoot整合Mongodb實現增刪查改的方法

    這篇文章主要介紹了SpringBoot整合Mongodb實現簡單的增刪查改,MongoDB是一個以分布式數據庫為核心的數據庫,因此高可用性、橫向擴展和地理分布是內置的,并且易于使用。況且,MongoDB是免費的,開源的,感興趣的朋友跟隨小編一起看看吧
    2022-05-05
  • Spring Boot多模塊化后,服務間調用的坑及解決

    Spring Boot多模塊化后,服務間調用的坑及解決

    這篇文章主要介紹了Spring Boot多模塊化后,服務間調用的坑及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • SpringSecurity注銷設置的方法

    SpringSecurity注銷設置的方法

    這篇文章主要為大家詳細介紹了SpringSecurity注銷設置的方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-09-09
  • Java微信公眾號安全模式消息解密

    Java微信公眾號安全模式消息解密

    這篇文章主要為大家詳細介紹了Java微信公眾號安全模式消息解密,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-12-12
  • Java全排列算法字典序下的下一個排列講解

    Java全排列算法字典序下的下一個排列講解

    今天小編就為大家分享一篇關于Java全排列字典序下的下一個排列,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-02-02
  • 一文讀懂JAVA中HttpURLConnection的用法

    一文讀懂JAVA中HttpURLConnection的用法

    這篇文章主要介紹了JAVA中的HttpURLConnection用法,文中講解非常細致,供大家參考和學習,感興趣的朋友可以了解下
    2020-06-06

最新評論