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

Java中ThreadLocal共享變量的使用

 更新時間:2024年01月02日 14:41:42   作者:EumenidesJ  
java.lang.ThreadLocal該類提供了線程局部變量,用于在當前線程中共享數(shù)據(jù),本文主要介紹了Java中ThreadLocal共享變量的使用,具有一定的參考價值,感興趣的可以了解一下

一、ThreadLocal

我們知道多線程訪問同一個共享變量時,會出現(xiàn)線程安全問題,為了保證線程安全開發(fā)者需要對共享變量的訪問操作進行適當?shù)耐讲僮鳎缂渔i等同步操作。

除此之外,Java提供了ThreadLocal類,當一個共享變量使用ThreadLocal聲明時,它表明,當每個線程訪問共享變量時,會把共享變量復制一份到線程的工作內存,之后線程對此共享變量進行操作時操作的都是線程工作內存的變量而不是主內存中的共享變量,從而不需要加鎖的同步操作實現(xiàn)避免出現(xiàn)線程安全問題。

二、Thread使用代碼示例

public class ThreadLocalTest {
    private static ThreadLocal<String> variable = new ThreadLocal<>(); // (1) 
 
    public static void main(String[] args) throws InterruptedException {
        variable.set(Thread.currentThread().getName());  // (2)
        // 創(chuàng)建線程一
        var thread1 = new Thread(() -> {
            System.err.println("Thread Name before set: " + Thread.currentThread().getName() + " " + variable.get());  // (3)
            variable.set(Thread.currentThread().getName()); // (4)
            System.err.println("Thread Name after set: " + Thread.currentThread().getName() + " " + variable.get()); // (5)
        });


        var thread2 = new Thread(() -> {
            System.err.println("Thread Name before set: " + Thread.currentThread().getName() + " " + variable.get()); // (6)
            variable.set(Thread.currentThread().getName()); // (7)
            System.err.println("Thread Name after set: " + Thread.currentThread().getName() + " " + variable.get()); // (8)
        });
        thread1.start();  // (9)
        thread2.start(); // (10)
        Thread.sleep(2000); // (11)
        System.err.println("main thread: " + variable.get()); // (12)
    }
}

輸出:

Thread2 before set: Thread-1 null
Thread2 after set: Thread-1 Thread-1
Thread1 before set: Thread-0 null
Thread1 after set: Thread-0 Thread-0
main thread: main

示例中我們創(chuàng)建了兩個線程,每個線程里都讀取和設置全局的ThreadLcoal變量:

代碼(1)創(chuàng)建了一個ThreadLocal共享變量variable,這里其實設置的是主線程工作內存里的共享變量副本

代碼(2)主線程設置ThreadLocal變量variable

代碼(3)線程一讀取共享變量variable的值

代碼(4)線程一設置共享變了variable的值,這里其實設置的是線程一工作內存里的共享變量副本

代碼(5)線程一再次讀取共享變量variable的值

代碼(6)線程二讀取共享變量variable的值

代碼(7)線程二設置共享變了variable的值,這里其實設置的是線程二工作內存里的共享變量副本

代碼(8)線程二再次讀取共享變量variable的值

代碼(9)啟動線程一

代碼(10)啟動線程二

代碼(11)主線程休眠2秒

代碼(12)主線程讀取共享變量variable的值

從輸出我們可以看到,每個兩個線程所操作的ThreadLocal變量互不影響,其實每個線程在設置和讀取共享變量variable時操作的都是共享變量在線程自己工作內存里的副本,并不會影響到其他線程的值。

三、ThreadLocal原理

我們說線程操作ThreadLocal類型的變量時,會復制一個變量副本到線程工作空間,然后所有操作都是對副本變量進行的。那線程是怎么復制ThreadLocal變量到線程工作空間的,線程和ThreadLocal之前是怎么關聯(lián)的。首先我們來看一看Thread的結構

Thread

可以看到Thread類有很多屬性,我們現(xiàn)在只關心threadLocalsinheritableThreadLocals,這兩個變量都是ThreadLocalMap類型的實例。TThreadLocalMap是一個ThreadLocal.ThreadLocalMap類型,這是一個特殊的Map。

首先看一下在前面的例子中我們是怎么在線程中使用ThreadLocal變量的,

variable.set(Thread.currentThread().getName()); // 設置ThreadLocal變量
variable.get();    // 讀取ThreadLocal變量

接下來我們看看ThreadLocal變量的set和get方法。

ThreadLocal.get()相關源碼如下:

public T get() {
  return get(Thread.currentThread()); // (1)
}

private T get(Thread t) {
  ThreadLocalMap map = getMap(t);  // (2)
  if (map != null) {
    ThreadLocalMap.Entry e = map.getEntry(this); // (3)
    if (e != null) {
      @SuppressWarnings("unchecked")
      T result = (T) e.value;
      return result;
    }
  }
  return setInitialValue(t);   // (4)
}

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

從代碼(1)可以看到,調用ThreadLocal的get()方法時,會將當前線程作為參數(shù)傳遞。代碼(2)調用getMap方法獲取ThreadLocalMap類型變量,如果map不為空則把ThreadLocal實例作為key獲取值,這個值就是ThreadLocal變量的值(5)可以看到getMap方法返回的就是Thread類型的threadLocals變量。根據(jù)上述分析我們可以知道:

線程在讀取ThreadLocal變量時,實際是獲取當前線程的threadLocals變量,然后把ThreadLocal實例當做key從threadLocals查詢對應的值。也就是說線程讀取的ThreadLocal的實際值并不是存在ThreadLocal實例里的,而是存在線程的threadLocals里面,threadLocals是一個ThreadLocal.ThreadLocalMap,這是一個特殊的Map,key為ThreadLocal實例,值為ThreadLocal變量的實際值。ThreadLoca相當于一個轉接口,連接Thread和ThreadLocal。

代碼(4)可以看到如果當前線程的threadLocals變量為null,會調用ThreadLocal的setInitialValue方法初始化當前線程的threadLocals實例。

private T setInitialValue(Thread t) {
  T value = initialValue();
  ThreadLocalMap map = getMap(t);
  if (map != null) {
    map.set(this, value);
  } else {
    createMap(t, value);
  }
  if (this instanceof TerminatingThreadLocal<?> ttl) {
    TerminatingThreadLocal.register(ttl);
  }
  if (TRACE_VTHREAD_LOCALS) {
    dumpStackIfVirtualThread();
  }
  return value;
}

void createMap(Thread t, T firstValue) {
  t.threadLocals = new ThreadLocalMap(this, firstValue); // (1)
}

setInitialValue方法會創(chuàng)建參數(shù)傳遞線程的threadLocals值,并且設置一個初始化值。從代碼(1)可以看到threadLocals的key為ThreadLocal實例。

下面再看看ThreadLocal的set方法:

public void set(T value) {
  set(Thread.currentThread(), value);   // (1)
  if (TRACE_VTHREAD_LOCALS) {
    dumpStackIfVirtualThread();
  }
}

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

從代碼(1)可以看到,調用ThreadLocald的set方法會向當前線程的threadLocals變量里設置傳遞的值value,key為ThreadLocal實例的引用,和get方法一樣,如果當前線程的threadLocals變量為null,則會創(chuàng)建一個ThreadLocalMap變量并把value設置為初始值。

總結:在每個線程內部都有一個threadLocals變量,該變量類型為ThreadLocal.ThreadLocalMap,其中key為我們定義的ThreadLocal變量的this引用,value則為我們使用set方法設置的值。每個線程的本地變量存放在線程自己的內存變量threadLocals中。

如果線程不銷毀,那么對應的本地變量就會一直存在,所以可能存在內存溢出,因此使用完畢之后要記得調用ThreadLocal的remove方法刪除對應線程的threadLocals變量里的值。

注意:ThreadLocal不具備繼承性,也就是說子線程并不能訪問父線程的ThreadLocal變量。

到此這篇關于Java中ThreadLocal共享變量的使用的文章就介紹到這了,更多相關Java ThreadLocal共享變量內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 淺談常用Java數(shù)據(jù)庫連接池(小結)

    淺談常用Java數(shù)據(jù)庫連接池(小結)

    這篇文章主要介紹了淺談常用Java數(shù)據(jù)庫連接池(小結),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-07-07
  • java 基礎知識之網(wǎng)絡通信(TCP通信、UDP通信、多播以及NIO)總結

    java 基礎知識之網(wǎng)絡通信(TCP通信、UDP通信、多播以及NIO)總結

    這篇文章主要介紹了java 基礎知識之網(wǎng)絡通信總結的相關資料,包括TCP通信、UDP通信、多播以及NIO,需要的朋友可以參考下
    2017-03-03
  • SpringCloud Eureka搭建的方法步驟

    SpringCloud Eureka搭建的方法步驟

    這篇文章主要介紹了SpringCloud Eureka搭建的方法步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-01-01
  • 一文教你學會搭建SpringBoot分布式項目

    一文教你學會搭建SpringBoot分布式項目

    這篇文章主要為大家詳細介紹了搭建SpringBoot分布式項目的相關知識,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2024-01-01
  • JSON.parseObject和JSON.toJSONString實例詳解

    JSON.parseObject和JSON.toJSONString實例詳解

    這篇文章主要為大家詳細介紹了JSON.parseObject和JSON.toJSONString實例,具有一定的參考價值,感興趣的朋友可以參考一下
    2018-06-06
  • Java實現(xiàn)經(jīng)典游戲之大魚吃小魚

    Java實現(xiàn)經(jīng)典游戲之大魚吃小魚

    這篇文章主要為大家詳細介紹了如何利用Java語言實現(xiàn)經(jīng)典游戲之大魚吃小魚,文中的示例代碼講解詳細,對我們學習Java游戲開發(fā)有一定幫助,需要的可以參考一下
    2022-08-08
  • 詳解領域驅動設計之事件驅動與CQRS

    詳解領域驅動設計之事件驅動與CQRS

    這篇文章分析了如何應用事件來分離軟件核心復雜度。探究CQRS為什么廣泛應用于DDD項目中,以及如何落地實現(xiàn)CQRS框架。當然我們也要警惕一些失敗的教訓,利弊分析以后再去抉擇正確的應對之道
    2021-06-06
  • Java Class 解析器實現(xiàn)方法示例

    Java Class 解析器實現(xiàn)方法示例

    這篇文章主要通過對class文件的分析,介紹了Java Class 解析器實現(xiàn)方法示例,具有一定參考價值,需要的朋友可以了解下。
    2017-09-09
  • 一文詳解Java中的包裝類和泛型

    一文詳解Java中的包裝類和泛型

    在Java中,由于基本類型不是繼承?Object,為了在泛型中可以?持基本類型,Java給每個基本類型都對應了?個包裝類型,有些情況下只有接收泛型才可以完成其功能,所以本文將給大家詳細講解一下Java中的包裝類和泛型,需要的朋友可以參考下
    2025-04-04
  • Spring中獲取Bean方法上的自定義注解問題解析

    Spring中獲取Bean方法上的自定義注解問題解析

    這篇文章主要介紹了Spring中如何獲取Bean方法上的自定義注解,基本的思路就是通過Spring提供的ApplicationContext#getBeansWithAnnotation+反射來實現(xiàn),需要的朋友可以參考下
    2023-06-06

最新評論