ThreadLocal学习
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学习的更多相关文章
- ThreadLocal学习记录
ThreadLocal简介 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的 ...
- Java ThreadLocal 学习
同步机制是为了同步多个线程对相同资源的并发访问,是为了多个线程之间进行通信的有效方式. 而ThreadLocal是隔离多个线程的数据共享,从根本上就不在多个线程之间共享资源(变量),这样当然不需要对多 ...
- ThreadLocal学习笔记
首先,ThreadLocal是Java语言提供的用于支持线程局部变量的标准实现类.很多时候,ThreadLocal与Synchronized在功能上有一定的共性,都可以用来解决多线程环境下线程安全问题 ...
- ThreadLocal 学习
JDK 1.2版本就已经提供了java.lang.ThreadLocal.其为多线程程序的并发问题提供了一种新的思路.使用该工具类可以简洁地编写出优美的多线程程序. 当使用ThreadLocal维护变 ...
- ThreadLocal学习资料
下面的这一段代码运行起来,就会发生线程安全问题: 启动两个线程,同时去修改 name 属性值. package com.liwei.thread; /** * 下面的代码演示了线程安全发生的由来 * ...
- java并发编程学习: ThreadLocal使用及原理
多线程应用中,如果希望一个变量隔离在某个线程内,即:该变量只能由某个线程本身可见,其它线程无法访问,那么ThreadLocal可以很方便的帮你做到这一点. 先来看一下示例: package yjmyz ...
- 2015年11月26日 Java基础系列(三)ThreadLocal类初级学习
序,ThreadLocal类是为了解决多线程的安全问题.线程安全的意思也就是说每个线程操作自己的变量,不要对其他线程的值造成影响. 在很多情况下,ThreadLocal比直接使用synchronize ...
- 深入学习ThreadLocal原理
上文我们学习了ThreadLocal的基本用法以及基本原理,ThreadLocal中的方法并不多,基本用到的也就get.set.remove等方法,但是其核心逻辑还是在定义在ThreadLocal内部 ...
- ThreadLocal<T>学习总结
public class ThreadLocalTest { /** * @param * @Author: xdj * @Date: 2019/4/12 10:16 * @Description: ...
随机推荐
- Margin和Padding之间的区别
margin ,padding body他们之间的区别就是 margin表示的是外边框的距离 padding表示的是内边框的距离 body表示的边框的距离
- Markdown 添加 Latex 数学公式
添加公式的方法 Latex 数学公式语法 添加公式的方法 行内公式 $行内公式$ 行间公式 $$行间公式$$ Latex 数学公式语法 角标(上下标) 上标命令^{} 下标命令_{} 上下标命令用来放 ...
- [Form Builder]内置函数execute_trigger、do_key详解
转:http://yedward.net/?id=82 1.execute_trigger:用来运行一个指定的触发器,常用来运行用户自定义的触发器. 语法:procedure execute_trig ...
- redhat 6.4 双网卡绑定
linux系统配置 1.redhat 6.4 双网卡绑定 1)#ethtool eth* //在服务器网口接网线至笔记本,确定各网口的配置文件: 2)切换目录 #cd /etc/sysconfig/n ...
- js事件3
一.loading——(用来加载位于网页中的文件,而非本地的) 例子: <!doctype html> <html lang="en"> <head& ...
- Android开发环境搭建(2015年8月更新)
1. 下载和安装Android SDK Android的官方站点是http://www.android.com: 登录https://developer.android.com/intl/zh-cn ...
- 周末充电之WPF(四).多窗口之间操作
多窗口实例: 1.在多个窗口的情况下如何自定义指定要启动的窗口程序 <Application x:Class="toolbar.App" xmlns="http:/ ...
- LigerUI API
参数列表 参数名 类型 描述 默认值 title String 表格标题 null width String|Int 宽度值,支持百分比 'auto' height String|Int 高度值,支持 ...
- python3实现的web端json通信协议
之前有用python3实现过tcp协议的,后来又实现了http协议的通信,今天公司想做一个功能自动测试系统, 下午弄了一会,发现json格式的实现可以更简单一点,代码如下:简单解说一下,一般与服务器通 ...
- mysql 主从 Got fatal error 1236 from master when reading data from binary log: 'Could not find first 错误
本地MySQL环境,是两台MySQL做M-M复制.今天发现错误信息: mysql 5.5.28-log> show slave status\G ************************ ...