1、简介:

类ThreadLocal<T>,为变量提供了线程本地化副本。对于用ThreadLocal维护的变量,当前线程中的副本不同于它在其他线程中的副本,每个线程通过ThreadLocal提供的get、set等方法来独立维护自己的变量副本。当多线程环境中的变量使用ThreadLocal维护时,ThreadLocal为每个使用该变量的线程分配一个独立的变量副本。

ThreadLocal实例在类中通常作为静态私有化类变量。

2、ThreadLocal接口:

public T get(); //返回被维护变量的变量的线程本地化的副本值
public void set(T value); //将当前线程中维护的本地化副本值设置为value
public void remove(); //移除当前线程中被维护变量的值 protected T initialValue(); //提供线程本地化对象的初始值

3、适用性:

ThreadLocal适用于为每个线程都提供一个固定的初始状态的情形,每个线程使用自己的本地化对象来完成自己的操作。

4、Demo:

对非线程安全对象的改造:

      public class TopicDao{
//一个非线程安全的变量
private Connection conn; public void addTopic(){
conn = DriverManager.getConnection(); //在多线程环境中使用非线程安全变量
Statement stat = conn.createStatement(); }
}

在使用JDBC来操作数据库时,Connection对象提供了所连接的数据库的表、支持的SQL语句等信息,但是Connection本身并不是一个线程安全变量,用于多线程环境中可能会出现问题。

改造: 使用ThreadLocal来为每个线程维护一个conn变量的线程本地化副本。

      public class TopicDao{
//使用ThreadLocal来保存Connection变量
private static ThreadLocal<Connection> connThreadLocal = new ThreadLocal<Connection>();
public static Connection getConnection(){
//如果connThreadLocal没有本线程对应的Connection,创建一个新的Connection,
//并将其保存到线程本地变量中
if(connThreadLocal.get() == null ){
Connection conn = ConnectionManager.getConnection();
connThreadLocal.set(conn);
return conn;
}else{
//直接返回线程本地变量
return connThreadLocal.get();
}
} public void addTopic(){
//从ThreadLocal中获取线程对象
Statement stat = getConnection().createStatement();
}
}

ThreadLocal使用方式

由例子中也可看出,ThreadLocal实例通常作为类的私有静态成员,并且提供一个静态方法来返回ThreadLocal所维护的线程本地化对象(该例中未conn)的值,同时在该静态方法中为每个线程提供该线程本地化对象的初始值。

5、ThreadLocal.java源文件:

     /**
* ThreadLocal内部包含一个用数组实现的哈希表,用来存储对应到每个线程的局部对象的值
* 其中,ThreadLocal对象担当key,实际通过threadLocalHashCode值来进行检索
*/
public class ThreadLocal<T> {
/**
* 存储用于确定哈希表的键值
*/
private final int threadLocalHashCode = nextHashCode(); /**
* 返回在当前线程中的线程局部对象的值,
* 若线程局部对象对于当前线程没有值,则被初始化微 initialValue方法的返回值
*/
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();
} /**
* 获取当前线程对应的ThreadLocalMap对象
* 若存在,则将当前线程与T类型变量value按照名值对的方式放置到ThreadLocalMap中;
* 若不存在,则创建当前ThreadLocalMap对象,并放入
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
//以当前线程对象为key,设置当前局部对象的值
map.set(this, value);
else
createMap(t, value);
} /**
* 移除当前线程中所保存的本地化对象的值
*/
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
//从ThreadLocalMap中移除对象
m.remove(this);
} /**
* 返回当前线程关联的ThreadLocalMap对象
* 查看Thread源码可知,每个Thread都含有ThreadLocal.ThreadLocalMap类型的threadLocals变量
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
} /**
* 创建当前线程关联的ThreadLocalMap对象
*/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
} /**
* ThreadLocalMap是一个定制的只能用来存储线程局部对象的哈希映射表
*
* 使用数组来作为哈希表存储结构
*
* 其中数组中元素类型为Entry,Entry为指向ThreadLocal的弱引用的子类,并且含有Object变量来维护与当前ThreadLocal对象关联的变量值
*
*/
static class ThreadLocalMap { static class Entry extends WeakReference<ThreadLocal> {
/**
* 维护与当前ThreadLocal相关联的变量值
*/
Object value; Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
} //哈希表的初始大小
private static final int INITIAL_CAPACITY = 16; /**
* 哈希表存储的数组
*
* 其中数组中元素类型为Entry,Entry为指向ThreadLocal的弱引用的子类,并且含有Object变量来维护与当前ThreadLocal对象关联的变量值
*/
private Entry[] table; /**
* ThreadLocalMap使用延迟初始化,当我们需要向ThreadLocalMap中放元素时,才会初始化它
*/
ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); //产生哈希值
table[i] = new Entry(firstKey, firstValue); //初始时,使用ThreadLocal.threadLocalHashCode作为哈希表的哈希值 size = 1; //设定ThreadLocalMap中元素个数
setThreshold(INITIAL_CAPACITY);
} /**
* 设置于当前key所关联的线程本地化对象的值
*
* 涉及到哈希表的冲突处理
*/
private void set(ThreadLocal key, Object value) {
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)]) {
ThreadLocal k = e.get();
if (k == key) {
e.value = value;
return;
} if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
} tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
} /**
* 删除ThreadLocal对应的线程本地化对象的值
*/
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;
}
}
}
}
}

根据ThreadLocal.java中列出的源码,可以走一遍ThreadLocal是如何进行set、get、remove操作的,可以加深对ThreadLocal对象的理解和其内部工作机制。

注:查看Thread源码可知,每个Thread类中都含有ThreadLocal.ThreadLocalMap类型的threadLocals变量(初始值为null)。

ThreadLocal学习的更多相关文章

  1. ThreadLocal学习记录

    ThreadLocal简介 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的 ...

  2. Java ThreadLocal 学习

    同步机制是为了同步多个线程对相同资源的并发访问,是为了多个线程之间进行通信的有效方式. 而ThreadLocal是隔离多个线程的数据共享,从根本上就不在多个线程之间共享资源(变量),这样当然不需要对多 ...

  3. ThreadLocal学习笔记

    首先,ThreadLocal是Java语言提供的用于支持线程局部变量的标准实现类.很多时候,ThreadLocal与Synchronized在功能上有一定的共性,都可以用来解决多线程环境下线程安全问题 ...

  4. ThreadLocal 学习

    JDK 1.2版本就已经提供了java.lang.ThreadLocal.其为多线程程序的并发问题提供了一种新的思路.使用该工具类可以简洁地编写出优美的多线程程序. 当使用ThreadLocal维护变 ...

  5. ThreadLocal学习资料

    下面的这一段代码运行起来,就会发生线程安全问题: 启动两个线程,同时去修改 name 属性值. package com.liwei.thread; /** * 下面的代码演示了线程安全发生的由来 * ...

  6. java并发编程学习: ThreadLocal使用及原理

    多线程应用中,如果希望一个变量隔离在某个线程内,即:该变量只能由某个线程本身可见,其它线程无法访问,那么ThreadLocal可以很方便的帮你做到这一点. 先来看一下示例: package yjmyz ...

  7. 2015年11月26日 Java基础系列(三)ThreadLocal类初级学习

    序,ThreadLocal类是为了解决多线程的安全问题.线程安全的意思也就是说每个线程操作自己的变量,不要对其他线程的值造成影响. 在很多情况下,ThreadLocal比直接使用synchronize ...

  8. 深入学习ThreadLocal原理

    上文我们学习了ThreadLocal的基本用法以及基本原理,ThreadLocal中的方法并不多,基本用到的也就get.set.remove等方法,但是其核心逻辑还是在定义在ThreadLocal内部 ...

  9. ThreadLocal<T>学习总结

    public class ThreadLocalTest { /** * @param * @Author: xdj * @Date: 2019/4/12 10:16 * @Description: ...

随机推荐

  1. iOS项目进行测试的配置

    一.首先,注册苹果开发者账号并登陆https://developer.apple.com/account/#/overview/ZFBE9NJZM6, 二.创建测试证书

  2. 20160331javaweb 之JSP page 指令

  3. Sublime Text2配置python环境

    1.下载python并安装     地址:https://www.python.org/downloads/ 2.下载Sublime Text2 并安装     地址:http://www.subli ...

  4. 无可匹敌的创建job(细化很多细节)

    declare  jobno           binary_integer ;  rm_days         number := 15; --保留多少天的数据,单位天数  rm_hour    ...

  5. IOS中的NSTimer定时器详解

    /* 在IOS中有多种定时器,这里我对NSTimer定时器做了一个简单的介绍.如果你是小白,你可能会从这篇文章中学习到一些知识,如果你是大牛,请别吝啬你的评论,指出我的不足,你的质疑是对我最大的帮助. ...

  6. 脱离Xcode,程序在模拟器中无法运行

    今天在调试项目的时候 突然发现,如果项目不通过Xcode启动而是直接通过模拟器进行启动,程序闪一下马上退出,并且不是闪退,而是跑到后台去了,并且后台的程序同样无法启动.找了好多解决办法,最后的解决方案 ...

  7. java web-----servelet

    1,定义servlet: 2,servlet生命周期: 3,HttpServlet常用方法: 4,servlet常用接口: 一,servlet定义: servlet是一个继承HttpServlet类的 ...

  8. 04_XML_04_XMLDTD语法

    [DTD语法约束细节] * 元素定义 * 属性定义 * 实体定义 [1.元素定义] 在DTD文档中使用ELEMENT声明一个XML元素,语法格式如下所示: <!ELEMENT   元素名称  元 ...

  9. Codevs 1702 素数判定 2(Fermat定理)

    1702 素数判定 2 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 传送门 题目描述 Description 一个数,他是素数么? 设他为P满足(P< ...

  10. Hdu 1452 Happy 2004(除数和函数,快速幂乘(模),乘法逆元)

    Problem Description Considera positive integer X,and let S be the sum of all positive integer diviso ...