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. 【原】Shell脚本-判断文件有无进而复制

    2016年7月5日某同学在群上求助要编一个判断文件或目录在某路径下有无进而有的就复制粘贴到另一路径下,无的则将代码中断(不往下执行命令)的脚本.逐一完善.模板如下(生产环境可用到路径环境变量) --- ...

  2. 关于FPGA异步时钟采样--结绳法的点点滴滴

    一.典型方法 典型方法即双锁存器法,第一个锁存器可能出现亚稳态,但是第二个锁存器出现亚稳态的几率已经降到非常小,双锁存器虽然不能完全根除亚稳态的出现(事实上所有电路都无法根除,只能尽可能降低亚稳态的出 ...

  3. 请编程实现:产生一个int数组,长度为100,并向其中随机插入1-100,并且不能重复(百度了一下,get一种高性能算法,非递归)

    网上找到一种更好的实现方式: (1)把N个数放到容器A(int数组)中. (2)从N个数中随机取出1个数放入容器B(int数组)中. (3)把容器A中最后一个数与随机抽取的数对调 或者 把容器A中最后 ...

  4. HTML5十五大新特性

    HTML5想必大家都很熟悉了.然而,你能准确地说出HTML5带来了哪些新特性吗?本文总结了HTML5带来的15项你必须知道的新特性. 一起来看下: 1.新的文档类型  (New Doctype) 目前 ...

  5. (转)Ilist 和list的区别归纳总结

    常见问题: Ilist <> 本身只是一个泛型接口, 既然是接口当然不能实例化,只能用如下方法 IList <Class1> IList11 =new List <Cla ...

  6. Force removing ActivityRecord,app died, no saved state报错的理解

    为什么说理解呢?出现这个问题,我的情景是,在activity里面直接起了一个Thread,线程里面进行了一个繁重的任务,当线程执行完后,这个activity也销毁了,界面显示的任务栈下面的一个活动.百 ...

  7. C# 类是怎么执行的?

    C# 类是怎么执行的? public class Person{ static person(){} //不写,默认也有个空的 public person(){}//不写,默认也有个空的 public ...

  8. JDBC——数据层DAO

    DAO:Data Access Object DAO 数据层 Service 逻辑业务层 View  视图层 entity   实体层 实现增.删.改.查的数据层 public class EmpDA ...

  9. javascript document对象 第21节

    <html> <head> <title>DOM对象</title> <style type="text/css"> t ...

  10. java新手笔记9 类的封装示例

    1.bank类 package com.yfs.javase; //类封装 public class BankCard { //属性 int balance;//默认0 实例变量 分配给每个对象一个 ...