ThreadLocal实现原理

一、ThreadLocal介绍

这是一个线程的局部变量。也就是说,只有当前线程可以访问。既然是只有当前线程可以访问的数据,自然是线程安全的。

为每一个线程分配不同的对象,需要在应用层面保证。ThreadLocal只是起到了简单的容器作用。

二、实现原理

1. 我们需要关注的是ThreadLocal的set()方法和get()方法。

set()方法

public void set(T value) {

Thread t = Thread.currentThread();

ThreadLocalMap map = getMap(t);

if (map != null)

map.set(this, value);

else

createMap(t, value);

}

在set时,首先获得当前线程对象,然后通过getMap()拿到线程的ThreadLocalMap,并将值设入ThreadLocalMap中。而ThreadLocalMap可以理解为一个Map(虽然不是,但是你可以把它简单地理解成HashMap),但是它是定义在Thread内部的成员。注意下面的定义是从Thread类中摘出来的:

ThreadLocal.ThreadLocalMap threadLocals = null;

而设置到ThreadLocal中的数据,也正是写入了threadLocals这个Map。其中,key为Thread-Local当前对象,value就是我们需要的值。而threadLocals本身就保存了当前自己所在线程的所有“局部变量”,也就是一个ThreadLocal变量的集合。

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

}

首先,get()方法也是先取得当前线程的ThreadLocalMap对象。然后,通过将自己作为key取得内部的实际数据。

2.Thread类会进行一些清理工作,其中就包括清理ThreadLocalMap

这些变量是维护在Thread类内部的(ThreadLocalMap定义所在类),这也意味着只要线程不退出,对象的引用将一直存在。

下面是Thread中的方法:

/**

* This method is called by the system to give a Thread

* a chance to clean up before it actually exits.

*/

private void exit() {

if (group != null) {

group.threadTerminated(this);

group = null;

}

/* Aggressively null out all reference fields: see bug 4006245 */

target = null;

/* Speed the release of some of these resources */

threadLocals = null;

inheritableThreadLocals = null;

inheritedAccessControlContext = null;

blocker = null;

uncaughtExceptionHandler = null;

}

所以当我们使用线程池的时候,那就意味着线程未必会退出(比如固定大小的线程池,线程总是存在)

如果这样,将一些大大的对象设置到ThreadLocal中(它实际保存在线程持有的thread-Locals Map内),可能会使系统出现内存泄露的可能(这里我的意思是:你设置了对象到ThreadLo-cal中,但是不清理它,在你使用几次后,这个对象也不再有用了,但是它却无法被回收)。

此时,如果你希望及时回收对象,最好使用ThreadLocal.remove()方法将这个变量移除。就像我们习惯性地关闭数据库连接一样。如果你确实不需要这个对象了,那么就应该告诉虚拟机,请把它回收掉,防止内存泄露。

另外一种有趣的情况是JDK也可能允许你像释放普通变量一样释放ThreadLocal。比如,我们有时候为了加速垃圾回收,会特意写出类似obj=null之类的代码。如果这么做,obj所指向的对象就会更容易地被垃圾回收器发现,从而加速回收。同理,如果对于ThreadLocal的变量,我们也手动将其设置为null,比如tl=null。那么这个ThreadLocal对应的所有线程的局部变量都有可能被回收。

要了解这里的回收机制,我们需要更进一步了解Thread.ThreadLocalMap的实现。之前我们说过,ThreadLocalMap是一个类似HashMap的东西。更精确地说,它更加类似WeakHashMap。ThreadLocalMap的实现使用了弱引用。弱引用是比强引用弱得多的引用。Java虚拟机在垃圾回收时,如果发现弱引用,就会立即回收。Thread-LocalMap内部由一系列Entry构成,每一个Entry都是WeakReference<ThreadLocal>:

static class Entry extends WeakReference<ThreadLocal<?>> {

/** The value associated with this ThreadLocal. */

Object value;

Entry(ThreadLocal<?> k, Object v) {

super(k);

value = v;

}

}

这里的参数k就是Map的key,v就是Map的value。其中k也就是ThreadLocal实例,作为弱引用使用(super(k)就是调用了WeakReference的构造函数)。因此,虽然这里使用ThreadLocal作为Map的key,但是实际上,它并不真的持有ThreadLocal的引用。而当ThreadLocal的外部强引用被回收时,ThreadLocalMap中的key就会变成null。当系统进行ThreadLocalMap清理时(比如将新的变量加入表中,就会自动进行一次清理,虽然JDK不一定会进行一次彻底的扫描,但显然在我们这个案例中,它奏效了),就会自然将这些垃圾数据回收。

三、对性能的影响

为每一个线程分配一个独立的对象对系统性能也许是有帮助的。当然了,这也不一定,这完全取决于共享对象的内部逻辑。如果共享对象对于竞争的处理容易引起性能损失,我们还是应该考虑使用ThreadLocal为每个线程分配单独的对象。

一个典型的案例就是在多线程下产生随机数。在多线程共享一个Random实例的情况下,总耗时达13秒之多(这里是指4个线程的耗时总和,不是程序执行的经历时间)。而在ThreadLocal模式下,仅耗时1.7秒左右。

时间: 2024-10-29 10:46:29

ThreadLocal实现原理的相关文章

ThreadLocal的原理、作用、使用弱引用原因、应用举例

一. 原理 ThreadLocal就是一个类,他有get.set方法,可以起到一个保存.获取某个值的作用.但是这个类的get.set方法有点特殊,各个线程调用他的get.set操作是互不干扰的,具体原因在于他的方法实现: public T get() { Thread t = Thread.currentThread(); //先确定调用我的线程 ThreadLocalMap map = getMap(t); //根据调用我的线程,找到这个线程的ThreadLocalMap对象 if (map

ThreadLocal 工作原理、部分源码分析

1.大概去哪里看 ThreadLocal 其根本实现方法,是在Thread里面,有一个ThreadLocal.ThreadLocalMap属性 ThreadLocal.ThreadLocalMap threadLocals = null; ThreadLocalMap 静态内部类维护了一个Entry 数组 private Entry[] table; 查看Entry 源码,它维护了两个属性,ThreadLocal 对象 与一个Object static class Entry extends W

对ThreadLocal实现原理的一点思考

前言 在<透彻理解Spring事务设计思想之手写实现>中,已经向大家揭示了Spring就是利用ThreadLocal来实现一个线程中的Connection是同一个,从而保证了事务.本篇博客将带大家来深入分析ThreadLocal的实现原理. ThreadLocal是什么.有什么.能做什么? ThreadLocal提供一个线程(Thread)局部变量,访问到某个变量的每一个线程都拥有自己的局部变量.说白了,ThreadLocal就是想在多线程环境下去保证成员变量的安全. ThreadLocal提

ThreadLocal设计原理

1. ThreadLocal 1.1 简介 ThreadLocal是线程内部的数据存储类,通过它可以指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取数据. 它能够满足以下需求: 同一个变量在不同的线程中需要有不同的副本 经常应用于static方法,无法在线程创建的时候赋值 1.2 应用场景 数据库连接池 Hibernate的session 其他线程不安全的类在多线程中不加锁使用 1.3 应用示例 时间解析类SimpleDateFormat是线程

ThreadLocal 应用原理解析与常见问题

ThreadLocal是大家比较常用到的,在多线程下存储线程相关数据十分合适.可是很多时候我们并没有深入去了解它的原理. 首选提出几个问题,稍后再针对这些问题一一解答. 提到ThreadLocal,大家常说ThreadLocal是弱引用,那么ThreadLocal究竟是如何实现弱引用的呢? ThreadLocal是如何做到可以当做线程局部变量的呢? 大家创建ThreadLocal变量时,为什么都要用static修饰? 大家争论不止的ThreadLocal内存泄漏是什么鬼? 进入正题,先简单了解下

ThreadLocal的原理

ThreadLocal是一个支持泛型的java类,抛开里面的静态内部类ThreadLocalMap不说,其实它没几行代码,不信,您自己去看看.它用来干啥?类上注释说的很明白: 它能让线程拥有了自己内部独享的变量 每一个线程可以通过get.set方法去进行操作 可以覆盖initialValue方法指定线程独享的值 通常会用来修饰类里private static final的属性,为线程设置一些状态信息,例如user ID或者Transaction ID 每一个线程都有一个指向threadLocal

Java中ThreadLocal无锁化线程封闭实现原理

虽然现在可以说很多程序员会用ThreadLocal,但是我相信大多数程序员还不知道ThreadLocal,而使用ThreadLocal的程序员大多只是知道其然而不知其所以然,因此,使用ThreadLocal的程序员很多时候会被它导入到陷进中去,其实java很多高级机制系列的很多东西都是一把双刃剑,也就是有利必有其弊,那么我们的方法是找到利和弊的中间平衡点,最佳的方式去解决问题. 本文首先说明ThreadLocal能做什么,然后根据功能为什么要用它,如何使用它,最后通过内部说明讲解他的坑在哪里,使

java ThreadLocal(应用场景及使用方式及原理)

尽管ThreadLocal与并发问题相关,可是很多程序猿只将它作为一种用于"方便传參"的工具,胖哥觉得这或许并非ThreadLocal设计的目的,它本身是为线程安全和某些特定场景的问题而设计的. ThreadLocal是什么呢. 每一个ThreadLocal能够放一个线程级别的变量,可是它本身能够被多个线程共享使用,并且又能够达到线程安全的目的,且绝对线程安全. 比如: public final static ThreadLocal<String> RESOURCE = n

ThreadLocal使用以及原理

介绍 ThreadLocal是一个用于创建线程局部变量的类.当前线程通过ThreadLocal的set()方法设置的变量只对当前线程可见,通过get()获取设置的变量. 使用 支持泛型 ThreadLocal<String> threadLocal = new ThreadLocal<>(); 当前线程通过ThreadLocal对象的set(value)/get()设置变量和获取设置的变量 threadLocal.set("jinshuai"); threadL