JDK源碼解析之 java.lang.ThreadLocal

此類提供線程局部變量。這些變量與普通變量不同,每個訪問一個線程(通過其getset方法)的線程 都有其自己的,獨立初始化的變量副本。 ThreadLocal實例通常是希望將狀態與線程關聯的類中的私有靜態字段(例如,用戶ID或事務ID)。

以射擊游戲舉例,游戲開始時,每個人能夠領到一把槍,槍把上有三個數字:子彈數、殺敵數、自己的命數,為其設置的初始值分別為100、0、10.設戰場上的每個人都是一個線程,那么這三個初始值寫在哪里呢?
如果每個線程都寫死這三個值,萬一將初始子彈數統一改成 1000發呢?
如果共享,那么線程之間的并發修改會導致數據不準確.
能不能構造這樣一個對象,將這個對象設置為共享變量,統一設置初始值,但是每個線程對這個值的修改都是互相獨立的.這個對象就是ThreadLocal

一、類定義

public class ThreadLocal<T> {}

…用來限制Class中的參數類型,確保Class中參數的一致性

二、實例變量和相關方法

//用于ThreadLocalMap
private final int threadLocalHashCode = nextHashCode();//下一個hash code,從0開始
private static AtomicInteger nextHashCode = new AtomicInteger();//hash增量
private static final int HASH_INCREMENT = 0x61c88647;//在獲取下一個hash code
private static int nextHashCode() {return nextHashCode.getAndAdd(HASH_INCREMENT);
}

三、內部類

內部類:ThreadLocalMap

ThreadLocalMap負責存儲ThreadLocal及其變量,即ThreadLocal對象本身作為鍵,ThreadLocal存儲的變量作為值。每個Thread對象在聲明一個ThreadLocal后會持有一個ThreadLocalMap的引用,來實現ThreadLocal的功能。

ThreadLocalMap持有一個內部類Entry,類似于HashMap.Node類,負責保存鍵值對。

static class Entry extends WeakReference<ThreadLocal<?>> {Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}
}

Entry繼承了WeakReference類,使Entry的鍵為弱引用。

看到這里很多人或許有這樣一個疑問:為什么Entry要繼承WeakReference?
既然將ThreadLocal聲明為弱引用,那么自然會聯想到和GC有關。

如果不聲明為弱引用,以最上面Test類的代碼為例,當我們將上述ThreadLocal類型的靜態變量tl設置為null時,Thread對象成員變量threadLocals依然保留有一個ThreadLocalMap,該Map中持有保存該ThreadLocalEntry,在這個線程運行期間無法GC,從而引發內存泄漏。所以,Entry的鍵要聲明為弱引用。

四、主要方法

1.get()

返回此線程局部變量的當前線程副本中的值。

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

首先是獲取當前運行線程對象,然后根據該線程對象找到對應的ThreadLocalMap

  • 如果找到了該線程對應的ThreadLocalMap,則通過當前ThreadLocal對象作為鍵查找Map中對應的Entry(鍵值對)對象
  • 如果查找結果不為null,則返回Entry對象的value。否則調用setInitialValue方法將當前ThreadLocal對象(this)和變量作為鍵值對存入ThreadLocalMap并返回變量。

2.initialValue()

為變量設置初始值,該方法的默認實現是:

protected T initialValue() {return null;
}

如果想要為該變量設置一個初始值,只需重寫該方法即可,例如:

@Override
protected String initialValue() {return "hello world";
}

3.set(T value)

get方法類似,set方法首先會獲取當前運行的Thread對象并通過該對象獲取對應的ThreadLocalMap,如果map為空,則為當前Thread對象新建一個ThreadLocalMap,否則直接將value放入map中。

public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);
}

4、remove()

同樣,獲取當前Thread對應的ThreadLocalMap,然后調用ThreadLocalMapremove方法移除ThreadLocal對象,無需通過弱引用機制對該ThreadLocal對象進行GC。

public void remove() {ThreadLocalMap m = getMap(Thread.currentThread());if (m != null)m.remove(this);
}

五、總結

ThreadLocal是如何做到為每一個線程維護變量的副本的呢?

ThreadLocal類中設置了一個Map,存儲每一個線程的變量的副本。

ThreadLocal使用場合主要解決多線程中數據數據因并發產生不一致問題。ThreadLocal為每個線程的中并發訪問的數據提供一個副本,通過訪問副本來運行業務,這樣的結果是耗費了內存,單大大減少了線程同步所帶來性能消耗,也減少了線程并發控制的復雜度。

Synchronized用于線程間的數據共享,而ThreadLocal則用于線程間的數據隔離。

Threadlocal底層是通過threadlocalMap進行存儲鍵值 每個ThreadLocal類創建一個Map,然后用線程的ID作為Map的key,實例對象作為Map的value,這樣就能達到各個線程的值隔離的效果。
ThreadLocal的作用是提供線程內的局部變量,這種變量在線程的生命周期內起作用,減少同一個線程內多個函數或者組件之間一些公共變量的傳遞的復雜度。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/535755.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/535755.shtml
英文地址,請注明出處:http://en.pswp.cn/news/535755.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

華爾街頂級大師胡立陽名言

1.不要聽“親朋好友”的話&#xff0c;他們只會讓你成為“平凡人”。 2.不要只會“用功讀書”&#xff0c;重要的是“要讀對書”。  3&#xff0e;不要只是“努力工作”&#xff0c;重要的是“做對工作”。   4.不要指示結交“志趣相投”的朋友&#xff0c;否則你永遠只看到…

JDK源碼解析之 Java.lang.Enum

Enum是一個特殊的類. 我們不能以class Xxx extends Enum的方式手動繼承, 必須寫成enum Xxx的形式; 然而這段枚舉類的定義在編譯之后又變回了class Xxx extends Enum. 一、類定義 public abstract class Enum<E extends Enum<E>>implements Comparable<E>, …

Linux下的一些簡單網絡配置命令介紹

1、 ifconfig可以使用ifconfig命令來配置并查看網絡接口的配置情況。例如&#xff1a;&#xff08;1&#xff09; 配置eth0的IP地址&#xff0c; 同時激活該設備。#ifconfig eth0 192.168.1.10 netmask 255.255.255.0 up&#xff08;2&#xff09; 配置eth0別名設備eth0:1的IP地…

JDK源碼解析之 java.lang.Throwable

在 Java 中&#xff0c;所有的異常都有一個共同的祖先 Throwable&#xff08;可拋出&#xff09;。Throwable 指定代碼中可用異常傳播機制通過 Java 應用程序傳輸的任何問題的共性。 一、類定義 public class Throwable implements Serializable {}Serializable&#xff1a;可…

JDK源碼解析之 java.lang.Error

java.lang.Error 錯誤。是所有錯誤的基類&#xff0c;用于標識嚴重的程序運行問題。這些問題通常描述一些不應被應用程序捕獲的反常情況。 一、源碼部分 //繼承了java.lang.Throwable public class Error extends Throwable {//適用于java序列化機制,過判斷類的serialVersionU…

linux命令之有關網絡的操作命令

1&#xff0e;hostname 命令&#xff08;1&#xff09;一般格式&#xff1a;hostname [選項] [主機名]&#xff08;2&#xff09;說明&#xff1a;顯示或設置系統的主機名&#xff1b;如果無任何選項和主機名&#xff0c;則用于顯示系統的主機名。&#xff08;3&#xff09…

JDK源碼解析之 java.lang.Exception

異常。是所有異常的基類&#xff0c;用于標識一般的程序運行問題。這些問題通常描述一些會被應用程序捕獲的反常情況。 一、源碼部分 //繼承了java.lang.Throwable public class Exception extends Throwable {//適用于java序列化機制,過判斷類的serialVersionUID來驗證的版本…

linux命令之有關關機和查看系統信息的命令

shutdown 正常關機 reboot 重啟計算機 ps 查看目前程序執行的情況top 查看目前程序執行的情景和內存使用情況kill 終止一個進程date 更改或查看目前時間 一&#xff0e;查看系統的進程 要管理進程&#xff0c;首先要知…

HDFS-文件讀寫過程

一、文件讀取 Client向NameNode發起RPC請求&#xff0c;來確定請求文件block所在的位置&#xff1b;NameNode會視情況返回文件的部分或者全部block列表&#xff0c;對于每個block&#xff0c;NameNode 都會返回含有該 block 副本的 DataNode 地址&#xff1b; 這些返回的 DN 地…

linux命令復習之有關磁盤空間的命令

1&#xff0e;mount 命令&#xff08;1&#xff09;一般格式&#xff1a;mount 文件系統類型 [選項] 掛接設備&#xff08;2&#xff09;說明&#xff1a;將某個文件系統掛載到某個目錄上。當這個命令執行成功后&#xff0c;直到使用 umount 將這個文件系統移除為止。&…

HDFS-常用API操作

一、Maven <dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>RELEASE</version> </dependency> <dependency><groupId>org.apache.logging.log4j</groupId><artifactId>…

linux命令之-管理文件和目錄的命令

一. 創建和刪除目錄的命令 1&#xff0e;mkdir 命令 &#xff08;1&#xff09;一般格式&#xff1a;mkdir [選項] 目錄名 &#xff08;2&#xff09;說明&#xff1a;該命令創建由目錄名命名的目錄。 &#xff08;3&#xff09;舉例1&#xff1a; 在目錄 /usr/fedora 下建…

Hive-簡介入門

Hive簡介 Hive最初是Facebook為了滿足對海量社交網絡數據的管理和機器學習的需求而產生和發展的。互聯網現在進入了大數據時代&#xff0c;大數據是現在互聯網的趨勢&#xff0c;而hadoop就是大數據時代里的核心技術&#xff0c;但是hadoop的mapreduce操作專業性太強&#xff0…

Hive-原理解析

一、Hive 架構 下面是Hive的架構圖。 Hive的體系結構可以分為以下幾部分 1、用戶接口&#xff1a;CLI&#xff08;hive shell&#xff09;&#xff1b;JDBC&#xff08;java訪問Hive&#xff09;&#xff1b;WEBUI&#xff08;瀏覽器訪問Hive&#xff09; 2、元數據&#x…

linux命令之history命令

在Linux系統上輸入命令并按下Enter后&#xff0c;這個命令就會存放在命令記錄表 ( ~/.bash_history )中&#xff0c;預定的記錄為1000條&#xff0c;這些都定義在環境變量中。列出所有的歷史記錄&#xff1a;#history 只列出最近10條記錄&#xff1a;#history 10 (注,history和…

Hive-配置安裝

一、HDFS安裝 1、解壓到指定位置tar -zxvf apache-hive-3.1.2-bin.tar.gz -C /usr/local/apps/ 2、改名mv apache-hive-3.1.2-bin/ hive-3.1.2 3、在conf目錄下添加Hadoop安裝路徑mv hive-env.sh.template hive-env.sh # 配置HADOOP_HOME路徑 export HADOOP_HOME/opt/module/…

linux文件系統概念目錄結構

文件系統概念一. 文件與目錄的定義1. 文件系統&#xff1a;它是磁盤上有特定格式的一片區域&#xff0c;操作系統通過文件系統可以方便地查尋和訪問其中所包含的磁盤塊&#xff1b;2. 文件&#xff1a;文件系統中存儲數據的一個命名的對象。3. 目錄&#xff1a;其中包含許多文件…

JDK源碼解析之 java.lang.Class

Java程序在運行時&#xff0c;Java運行時系統一直對所有的對象進行所謂的運行時類型標識。 這項信息紀錄了每個對象所屬的類。虛擬機通常使用運行時類型信息選準正確方法去執行&#xff0c;用來保存這些類型信息的類是Class類。Class類封裝一個對象和接口運行時的狀態&#xff…

Linux Vi常用技巧

VI常用技巧VI命令可以說是Unix/Linux世界里最常用的編輯文件的命令了&#xff0c;但是因為它的命令集眾多&#xff0c;很多人都不習慣使用它&#xff0c;其實您只需要掌握基本命令&#xff0c;然后加以靈活運用&#xff0c;就會發現它的優勢&#xff0c;并會逐漸喜歡使用這種方…

JDK源碼解析之 java.lang.ClassLoader

Class代表它的作用對象是類&#xff0c;Loader代表它的功能是加載&#xff0c;那么ClassLoader就是把一個以.class結尾的文件以JVM能識別的存儲形式加載到內存中。 一、核心方法 1、loadClass方法 protected Class<?> loadClass(String name, boolean resolve) throws…