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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

线程安全的map_ThreadLocal | 线程本地存储

發布時間:2025/4/16 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 线程安全的map_ThreadLocal | 线程本地存储 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

并發場景下,多個線程同時讀寫共享變量就有可能產生并發安全問題。反過來也可以說,不存在共享變量,就不會出現線程安全問題。Java中有兩種常用的避免共享變量的方法,使用局部變量,以及使用 ThreadLocal。

局部變量存在于每個線程內部的調用棧中,多個線程之間互相訪問不到對方的局部變量,這就叫做線程封閉。如下圖所示,局部變量存在于線程各自的調用棧中,線程之間互不打擾。

采用局部變量的方案,的確避免了變量被多個線程共享,同時它也禁止同一個線程中不同方法共享這個變量。然而,單線程中不同的方法之間共享變量是不會導致線程安全問題的。


如果想讓同一個線程,不同的方法共享變量就可以使用 ThreadLocal,Java 提供的線程本地存儲方案。ThreadLocal 可以保證同一個變量,該線程中的方法看到的值是一樣,不同線程之間卻是隔離。


ThreadLocal 的使用方法

常規使用 ThreadLocal 的方式很簡單,創建一個 ThreadLocal 對象,然后調用它的 set(value)方法設置值,再調用 get() 方法獲取這個 ThreadLocal 對象對應的value。

// 創建一個 ThreadLocal ThreadLocal<String> tl = new ThreadLocal<>(); // set方法 tl.set("深頁"); // get方法 tl.get();

ThreadLocal 類的注釋中還帶有為每個線程分配自增 id 的示例代碼。withInitial()方法會調用initialValue()方法,為 ThreadLocal 設置 get() 的初始值。執行下面的代碼,可以看到每個類都有自己的id,并且id的自增的。

public class ThreadId {// Integer類型的原子類,用來分配Id,保證其本身是線程安全的private static final AtomicInteger nextId = new AtomicInteger();// 創建一個 ThreadLocal 變零,并且為其賦值private static ThreadLocal <Integer> threadId = ThreadLocal.withInitial(() -> nextId.getAndIncrement());// 獲取id,即從ThreadLocal中獲取對應的值public static int getId() {return threadId.get();}public static void main(String[] args) {for (int i = 0; i < 100; i++) {new Thread(() ->System.out.println(Thread.currentThread().getName()+ ": " + ThreadId.getId())).start();}} }

ThreadLocal還有一個經典的使用案例,就是將線程不安全的 SimpleDateFormat 類封裝成線程安全的,原理其實和上面的例子是一樣:

static class SafeDateFormat {static final ThreadLocal<DateFormat> tl = ThreadLocal.withInitial(()-> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));static DateFormat get() {return tl.get();} }

ThreadLocal的底層原理

先來看 set() 方法:

  • 首先獲取當前線程,然后通過當前線程獲取線程持有的局部變量 threadLocals
  • 如果返回的 map 不是空的就設置值
  • 如果返回的 map 是空的,就調用構造方法初始化 map 并為其設置值
  • public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value); }

    再看 get() 方法:

    public T get() {Thread t = Thread.currentThread();// getMap()返回當前線程的threadLocalsThreadLocalMap map = getMap(t);if (map != null) {// map存在返回當前ThreadLocal對應的value值ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}// map不存在就初始化return setInitialValue(); }

    看過上面兩個方法,可以看到它們除了涉及到 Thread 類,還涉及到了一個類 ThreadLocalMap。那么 Thread、ThreadLocal、ThreadLocalMap 之間是什么關系呢?

    ThreadLocalMap 是 ThreadLocal 的靜態內部類,ThreadLocalMap 的底層是一個 Entry[] table 數組,Entry 是 ThreadLocalMap 的靜態內部類,以 ThreadLocal 作為 key,以設置的值作為 value,如下所示:

    static class Entry extends WeakReference <ThreadLocal<?>> {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;} }

    Thread 持有一個 ThreadLocalMap 的引用 threadLocals:

    ThreadLocal.ThreadLocalMap threadLocals = null;

    所以說,我們通過當前線程 Thread t 可以到 t 持有的 ThreadLocalMap,并且通過 ThreadLocal 對象返回其對應的 value。

    ThreadLocal 內存泄露問題

    使用 Thread.start() 方法是不會產生內存泄露的問題的,只有當我們在線程池中使用 ThreadLocal 才有可能產生內存泄露問題。

    內存泄露的本質是長生命周期的對象,持有短生命周期對象。當短生命周期的對象使用結束之后,理應被垃圾回收器回收,但是它卻被一個更長生命周期的對象引用。通過可達性分析算法,該短生命周期的對象被一個GC Root引用,理應被回收的它就無法被回收。

    **那為什么在線程池中使用ThreadLocal就可能發生內存泄露的問題呢?**我們就從長生命周期的對象,持有短生命周期對象這個角度進行分析。

    線程池作為一種池化資源技術,目的是避免線程的頻繁創建和銷毀。一般來說,線程池中的線程生命周期都很長,是和應用程序同生共死的。這就意味著,被 Thread 持有的 ThreadLocalMap 一直都不會被回收。

    ThreadLocalMap 底層是一個 Entry 數組,Entry是<ThreadLocal,value>對結構。Entry 對 ThreadLocal 是弱引用(WeakReference),所以ThreadLocal 生命周期之后,是結束是可以被回收掉的。但是 Entry 對 value 強引用的,所以即便 Value 的生命周期結束了,Value 也是無法被回收的,從而導致內存泄露。

    InheritableThreadLocal 與繼承性

    使用 ThreadLocal 還有這樣一種需求,ThreadLocal 創建了線程變量 V,然后希望該線程創建的子線程也能訪問到父線程的線程變量 V。

    為此 Java 提供了 InheritableThreadLocal 來支持這種特性,InheritableThreadLocal 繼承自 ThreadLocal,用法其實和 ThreadLocal 一樣。

    public class InheritableThreadLocal<T> extends ThreadLocal<T> {

    最后做一個小結,多線程同時讀寫共享變量就有可能產生并發問題。一種解決并發問題的思路就是避免變量被共享。與之對應的技術有線程隔離(局部變量),以及線程本地存儲ThreadLocal。

    相比于使用局部變量,ThreadLocal 存儲的變量可以供線程中的方法共享,單線程對共享變量的讀寫必定是線程安全的。

    總結

    以上是生活随笔為你收集整理的线程安全的map_ThreadLocal | 线程本地存储的全部內容,希望文章能夠幫你解決所遇到的問題。

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

    主站蜘蛛池模板: 熟妇人妻一区二区三区四区 | 桃色一区二区三区 | 亚洲欧美中文日韩在线v日本 | 成人久久毛片 | 99热久久这里只有精品 | 亚洲无限av | 国产热热 | 五月涩 | 国产日韩在线观看一区 | 一级不卡| 91九色丨porny丨肉丝 | 亚洲精品白虎 | 毛片网止 | 无码精品视频一区二区三区 | 黄网在线观看免费 | 99精品国产99久久久久久97 | 精品国产视频在线 | 农村搞破鞋视频大全 | 黄色片一级 | 日本一区二区三区视频在线播放 | 亚洲一区二区人妻 | 亚洲性视频在线 | 精品国产一区二区三区在线 | 五月婷婷丁香网 | 免费国产在线观看 | 国产美女一区二区 | 亚洲精品美女在线观看 | 亚洲av成人无码一区二区三区在线观看 | 男生和女生一起差差差视频 | 玩日本老头很兴奋xxxx | 五月婷视频 | 欧美日韩国产电影 | 可以免费看的av | 91国内视频| 五月天综合激情 | 国产超碰人人模人人爽人人添 | 99人妻碰碰碰久久久久禁片 | 精品久久无码中文字幕 | 久久久经典 | av色欲无码人妻中文字幕 | 91丨九色丨丰满人妖 | 国产成人精品亚洲日本在线观看 | 欧美日韩在线视频免费 | 国产高清一区在线观看 | 99热r| 欧美精品一区二区三区久久久竹菊 | 在线观看免费高清在线观看 | 日本暧暧视频 | 人妻天天爽夜夜爽一区二区三区 | 色综合免费视频 | 老司机深夜福利在线观看 | 亚洲aaaa级特黄毛片 | 久久这里只精品 | 欧美性生话 | 麻豆一区产品精品蜜桃的特点 | 久久综合色综合 | 久久精品一区二区三区黑人印度 | 精品日韩制服无码久久久久久 | 日韩城人视频 | 日韩激情小说 | 欧美性一级片 | 国产高清精品在线 | 国产精品sm调教免费专区 | 国产精品久久久久三级 | 99精品久久精品一区二区 | 亲子乱aⅴ一区二区三区 | 天堂在线中文网 | jzzijzzij亚洲成熟少妇在线播放 狠狠躁日日躁夜夜躁2022麻豆 | 亚洲激情av | 久久久免费在线观看 | 亚洲老女人 | 97久久精品视频 | 欧美日韩一区二区三区四区 | 久久久久久久穴 | 永久免费毛片 | 日本一区二区三区精品视频 | 在线资源av | 91亚洲精品在线观看 | 538精品在线视频 | 91人人视频 | 久久黄色 | 邻居交换做爰2 | 少妇媚药按摩中文字幕 | www久久久久久久 | 色在线影院 | 久久94| 99这里只有精品 | 337p亚洲精品色噜噜噜 | 成人免费播放视频 | 精品一区二区三区免费 | 精品一区二区在线视频 | 99视频免费在线观看 | 中文字字幕在线观看 | 隔壁人妻偷人bd中字 | 国产在线中文字幕 | 国产欧美一级片 | 亚洲精品三区 | ass亚洲肉体欣赏pics | 青青草激情 |