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.mzph.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…