ThreadLocal的概念

  ThreadLocal从英文的角度看,可以看成thread和local的组合,就是线程本地的意思,我们都知道,看过jvm内存分配的人都知道在jvm虚拟机中对每一个线程都分配了一个独立的空间。独立的空间就意味着线程之间是相互隔离的。那么如果在独立空间里面声明某一些东西(也就是线程内部的东西),这个就可以变向的解决多线程程序并发问题了(个人理解)。这个东西就是ThreadLocal,所以说ThreadLocal可以为解决多线程的并发问题提供一种新的思路。稍后threadLocal使用一节会讲解。学过java的人都知道ThreadLocal变量是为使用它的线程提供一个单独的线程局部变量值的副本。所以每个线程都可以独立的改变自己的副本而不会影响其他线程所对应的副本。

  说明:在ThreadLocal源码的时候,有一个注释我在这里引用下,如下图。翻译过来就是:该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。

  

This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID). 

  从上面的话中我们可以得出以下几点信息

  (1) ThreadLocal是一个线程局部的变量。

  (2) ThreadLocal 独立于变量的初始化副本,在看ThreadLocal源码的时候会有一个initialValue(),也就是说ThreadLocal可以初始化一个值,然后某个线程可以获取到这个初始化值的副本。

  (3) 状态和某一个线程关联,因为ThreadLocal不是是用于共享变量所设计的,而且为了方便线程处理自己的状态而引入的。

Thread关于ThreadLocal讲解

  java线程知识太多了,本文只是谈谈ThreadLocal理解,在Thread.java代码中,我们可以看到声明了两个关于ThreadLocal.ThreaLocalMap的变量,ThreadLocalMap主要是用来存放local变量的,以后每个Thread要访问local对象的时候,那么就会使用这两个声明的变量。很多人会问,为什么Thread里面会引用两个ThreadLocalMap对象呢,这两个对象又有什么区别呢?这个问题留给自己去想想,我就不在这里说了,看看字面意思应该就明白了。

 

 /* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null; /*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

ThreadLocal源码解析:

  (1) 构造函数

    

    /**
* Creates a thread local variable.
*/
public ThreadLocal() {
}

  (2) initialValue 初始化值

    这个是一个初始化值的函数,是保护类型的,很明显,作者的意图是想要重载此函数。基本情况下,此函数只会调用一次。在调用第一次调用get方法的时候会调用这个方法。

 protected T initialValue() {
return null;
}

  具体用户如下:

   

    private static final ThreadLocal<DateFormat> dataFormat = new ThreadLocal<DateFormat>(){
protected DateFormat initialValue() {
// 初始化值
return new SimpleDateFormat("yyyy-HH-MM");
}
};

  (2) set方法

    

    /**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
} 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,还记得在前面说的Thread类里面声明了两个变量吗?这里很巧妙的是,ThreadLocalMap就是获取当前线程里面的声明的ThreadLocalMap,所以说ThreadLocal是线程的局部变量。如果

map为空,当前会创建一个map。

 (3)get方法

  

  public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
} /**
* Variant of set() to establish initialValue. Used instead
* of set() in case user has overridden the set() method.
*
* @return the initial value
*/
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}

  get方法没有什么讲的,就是设置而已。

 (4) remove方法

  

    /**
* Remove the entry for key.
*/
private void remove(ThreadLocal key) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
if (e.get() == key) {
e.clear();
expungeStaleEntry(i);
return;
}
} private int expungeStaleEntry(int staleSlot) {
Entry[] tab = table;
int len = tab.length; // expunge entry at staleSlot
tab[staleSlot].value = null;
tab[staleSlot] = null;
size--; // Rehash until we encounter null
Entry e;
int i;
for (i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
ThreadLocal k = e.get();
if (k == null) {
e.value = null;
tab[i] = null;
size--;
} else {
int h = k.threadLocalHashCode & (len - 1);
if (h != i) {
tab[i] = null; // Unlike Knuth 6.4 Algorithm R, we must scan until
// null because multiple entries could have been stale.
while (tab[h] != null)
h = nextIndex(h, len);
tab[h] = e;
}
}
}
return i;
}

  remove它提供了移除此线程局部变量在当前进程的值,在ThreadLocalMap的remove方法会调用expungeStaleEntry的方法,这个方法作用很大,它会把ThreadLocalMap里面保存的key为空的值置空。方便GC处理。所以说在很多人都说在ThreadLocal保存的值在不使用的情况下,最好调用remove方法。

  ThreadLocal可能引起的内存泄露

    ThreadLocal其实里面存放线程局部变量的是ThreadLocalMap,ThreadLocalMap是一个什么东西呢?字面意思就可以看出是一个map。它主要是用来存放local信息的,我们存放的时候就是以ThreadLocal为key来进行存放的。从源码中可以看出,key是一个弱引用类型。当把threadlocal实例置为null以后,没有任何强引用指向threadlocal实例,所以threadlocal将会被gc回收. 但是,我们的value却不能回收,因为存在一条从current thread连接过来的强引用. 只有当前thread结束以后, current thread就不会存在栈中,强引用断开, Current Thread, Map, value将全部被GC回收。下面我们来证明下,请看下面代码。

package com.zh.test;

import java.lang.ref.WeakReference;

import com.zh.test.MyThreadLocal.ThreadLocalMap;

public class Test1 {
public static void main(String[] args) {
MyThreadLocal myThreadLocal = new MyThreadLocal();
myThreadLocal.set(new Object());
ThreadLocalMap threadLocalMap = myThreadLocal.getThreadLocalMap();
threadLocalMap.printAll();
myThreadLocal = null;
System.gc();
threadLocalMap.printAll();
} } class MyThreadLocal{ static ThreadLocalMap threadLocalMap = new ThreadLocalMap();
public void set(Object vData){
threadLocalMap.set(this, vData);
} public ThreadLocalMap getThreadLocalMap(){
return threadLocalMap;
} static class ThreadLocalMap {
private Entry[] table = new Entry[1]; static class Entry extends WeakReference<MyThreadLocal> {
Object value; Entry(MyThreadLocal k, Object v) {
super(k);
value = v;
}
} private void set(MyThreadLocal key, Object value) {
table[0] = new Entry(key, value);
} public void printAll(){
for (Entry entry : table) {
System.out.println(entry.get());
System.out.println(entry.value);
}
}
}
}

  运行上面代码,会发现,在运行GC以前没有任何变化,在运行GC以后,发现key为空了,但是value值却还存在,这说明直接将threadLocal置空,这个方法是不正确的,这个会引起内存泄露的。所以说千万不要把ThreadLocal置空,那么应该如何处理呢,在前面已经说了,在不用了ThreadLocal的时候直接调用remove方法。这样的话内部会把key为空的value值也清楚了。 

 

ThreadLocal的使用

  ThreadLocal如何使用呢,在ThreadLocal的源码注释中,就交你如何使用了,本文就不交代了,有时间看看源码,会有意想不到的收获。

  ps:可以把一些线程不安全的东西放在ThreadLocal里面比如说dateFormat,数据库连接。。。。。。。

 

我是如何理解ThreadLocal的更多相关文章

  1. 【Java】深入理解ThreadLocal

    一.前言 要理解ThreadLocal,首先必须理解线程安全.线程可以看做是一个具有一定独立功能的处理过程,它是比进程更细度的单位.当程序以单线程运行的时候,我们不需要考虑线程安全.然而当一个进程中包 ...

  2. 理解ThreadLocal背后的概念

    介绍 我之前在任何场合都没有使用过thread local,因此没有注意到它,直到最近用到它的时候. 前提信息 线程可以理解为一个单独的进程,它有自己的调用栈.在java中每一个线程都有一个调用栈或者 ...

  3. 简单理解ThreadLocal原理和适用场景

    https://blog.csdn.net/qq_36632687/article/details/79551828?utm_source=blogkpcl2 参考文章: 正确理解ThreadLoca ...

  4. CSharpGL(55)我是这样理解PBR的

    CSharpGL(55)我是这样理解PBR的 简介 PBR(Physically Based Rendering),基于物理的渲染,据说是目前最先进的实时渲染方法.它比Blinn-Phong方法的真实 ...

  5. 我是如何理解并使用maven的

    前言 一直想写一篇关于Maven的文章,但是不知如何下笔,如果说能使用,会使用Maven的话,一.两个小时足矣,不需要搞懂各种概念.那么给大家来分享下我是如何理解并使用maven的. 什么是Maven ...

  6. vue是一个渐进式的框架,我是这么理解的

    vue是一个渐进式的框架,我是这么理解的 原文地址 时间:2017-10-26 10:37来源:未知 作者:admin 每个框架都不可避免会有自己的一些特点,从而会对使用者有一定的要求,这些要求就是主 ...

  7. 我是这样理解EventLoop的

    我是这样理解EventLoop的 一.前言   众所周知,在使用javascript时,经常需要考虑程序中存在异步的情况,如果对异步考虑不周,很容易在开发中出现技术错误和业务错误.作为一名合格的jav ...

  8. 理解ThreadLocal(之二)

    想必很多朋友对ThreadLocal并不陌生,今天我们就来一起探讨下ThreadLocal的使用方法和实现原理.首先,本文先谈一下对ThreadLocal的理解,然后根据ThreadLocal类的源码 ...

  9. 理解ThreadLocal(之一)

    ThreadLocal是什么 在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路.使用这个工具类可以很简洁地编 ...

随机推荐

  1. c# 文件同步服务器,iis 集群 ,代码同步(二)

    打算自己做一个 集群 代码同步软件. 基于Socket 和 FileSystemWatcher .

  2. MVC验证13-2个属性至少输入一项

    原文:MVC验证13-2个属性至少输入一项 有时候,我们希望2个属性中,至少有一个是必填,比如: using Car.Test.Portal.Extension;   namespace Car.Te ...

  3. inux上iptables防火墙的基本应用教程

    iptables是Linux上常用的防火墙软件,下面vps侦探给大家说一下iptables的安装.清除iptables规则.iptables只开放指定端口.iptables屏蔽指定ip.ip段及解封. ...

  4. .net下二进制序列化的格式分析[转]

    .net下二进制序列化的格式分析[转] -- 综合应用 (http://www.Host01.Com/article/Net/00020003/) --- .net下二进制序列化的格式分析 (http ...

  5. navigator,JS检测浏览器插件

    最早由Netscape Navigator 2.0引入的navigator对象,现在已经成为识别客户端浏览器的事实标准.虽然其它浏览器也通过其它方式提供了相同或相似的信息(例如,IE中的window. ...

  6. 重新想象 Windows 8.1 Store Apps (81) - 控件增强: 加载本地 html, 智能替换 html 中的 url 引用, 通过 Share Contract 分享 WebView 中的内容, 为 WebView 截图

    原文:重新想象 Windows 8.1 Store Apps (81) - 控件增强: 加载本地 html, 智能替换 html 中的 url 引用, 通过 Share Contract 分享 Web ...

  7. 为JS字符类型添加trim方法

    JS字符串本身并不没有提供常用的trim方法,我们可以通过修改String原形来添加此方法,我们就可以直接调用此方法了: String.prototype.trim = function(){retu ...

  8. 2.2 LINQ中使用from子句指定数据源

    数据源是LINQ查询中必不可少的元素,数据源是实现泛型接口IEnumerable<T>或IQueryable<T>的类对象. 可以将IEnumerable<T>简单 ...

  9. 读书笔记—CLR via C#章节1-2

    这本书这几年零零散散读过两三遍了,作为经典书籍,应该重复读反复读,既然我现在开始写博了,我也准备把以前觉得经典的好书重读细读一遍,并且将笔记整理到博客中,好记性不如烂笔头,同时也在写的过程中也可以加深 ...

  10. jQuery 1.9 移除了 $.browser 的替代方法

    jQuery 从 1.9 版开始,移除了 $.browser 和 $.browser.version , 取而代之的是 $.support . 在更新的 2.0 版本中,将不再支持 IE 6/7/8. ...