在JDK的早期版本中,提供了一种解决多线程并发问题的方案:java.lang.ThreadLocal类。ThreadLocal类在维护变量时,实际使用了当前线程(Thread)中的一个叫做ThreadLocalMap的独立副本,每个线程可以独立修改属于自己的副本而不会互相影响,从而隔离了线程和线程,避免了线程访问实例变量发生冲突的问题。

  ThreadLocal本身并不是一个线程,而是通过操作当前线程中的一个内部变量来达到与其他线程隔离的目的。之所以取名为ThreadLocal,所期望表达的含义是其操作的对象是线程的一个本地变量。

Thread.java

public class Thread implements Runnable {
// 这里省略了许多其他的代码
ThreadLocal.ThreadLocalMap threadLocals = null;
}

ThreadLocal.java

public class ThreadLocal<T> {
// 这里省略了许多其他代码
// 将value 的值保存于当前线程的本地变量中
public void set(T value) {
// 获取当前线程
Thread t = Thread.currentThread();
// 调用getMap 方法获得当前线程中的本地变量ThreadLocalMap
ThreadLocalMap map = getMap(t);
// 如果ThreadLocalMap 已存在,直接使用
if (map != null)
// 以当前的ThreadLocal 的实例作为key,存储于当前线程的
// ThreadLocalMap 中,如果当前线程中定义了多个不同的ThreadLocal
// 的实例,则它们会作为不同key 进行存储而不会互相干扰
map.set(this, value);
else
// 如果ThreadLocalMap 不存在,则为当前线程创建一个新的
createMap(t, value);
} // 获取当前线程中以当前ThreadLocal 实例为key 的变量值
public T get() {
// 获取当前线程
Thread t = Thread.currentThread();
// 获取当前线程中的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
// 获取当前线程中以当前ThreadLocal 实例为key 的变量值
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T) e.value;
}
// 当map 不存在时,设置初始值
return setInitialValue();
} // 从当前线程中获取与之对应的ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
} // 创建当前线程中的ThreadLocalMap
void createMap(Thread t, T firstValue) {
// 调用构造函数生成当前线程中的ThreadLocalMap
t.threadLocals = new ThreadLocalMap(this, firstValue);
} // ThreadLoaclMap 的定义
static class ThreadLocalMap {
//这里省略了许多代码
}
}
  • ThreadLocalMap变量属于线程的内部属性,不同的线程拥有完全不同的ThreadLo-calMap变量。
  • 线程中的ThreadLocalMap变量的值是在ThreadLocal对象进行set或者get操作时创建的。
  • 在创建ThreadLocalMap之前,会首先检查当前线程中的ThreadLocalMap变量是否已经存在,如果不存在则创建一个;如果已经存在,则使用当前线程已创建的ThreadLo-calMap。
  • 使用当前线程的ThreadLocalMap的关键在于使用当前的ThreadLocal的实例作为key进行存储。

ThreadLocal模式至少从两个方面完成了数据访问隔离,即横向隔离和纵向隔离。

  • 纵向隔离——线程与线程之间的数据访问隔离。这一点由线程的数据结构保证。因为每个线程在进行对象访问时,访问的都是各个线程自己的ThreadLocalMap。
  • 横向隔离——同一个线程中,不同的Thread-Local实例操作的对象之间相互隔离。这一点由ThreadLocalMap在存储时采用当前ThreadLocal的实例作为key来保证。

深入比较ThreadLocal模式与synchronized关键字

  • ThreadLocal是一个Java类,通过对当前线程中的局部变量的操作来解决不同线程的变量访问的冲突问题。所以,ThreadLocal提供了线程安全的共享对象机制,每个线程都拥有其副本。
  • Java中的synchronized是一个保留字,它依靠JVM的锁机制来实现临界区的函数或者变量在访问中的原子性。在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。此时,被用作“锁机制”的变量是多个线程共享的。
  • 同步机制(synchronized关键字)采用了“以时间换空间”的方式,提供一份变量,让不同的线程排队访问。而ThreadLocal采用了“以空间换时间”的方式,为每一个线程都提供一份变量的副本,从而实现同时访问而互不影响。

要完成ThreadLocal模式,其中最关键的地方就是创建一个任何地方都可以访问到的ThreadLocal实例。而这一点,我们可以通过类变量来实现,这个用于承载类变量的类就被视作是一个共享环境。

public class Counter {
// 新建一个静态的ThreadLocal 变量,并通过get 方法将其变为一个可访问的对象
private static ThreadLocal<Integer> counterContext = new
ThreadLocal<Integer>() {
protected synchronized Integer initialValue() {
return 10;
}
}; // 通过静态的get 方法访问ThreadLocal 中存储的值
public static Integer get() {
return counterContext.get();
} // 通过静态的set 方法将变量值设置到ThreadLocal 中
public static void set(Integer value) {
counterContext.set(value);
} // 封装业务逻辑,操作存储于ThreadLocal 中的变量
public static Integer getNextCounter() {
counterContext.set(counterContext.get() + 1);
return counterContext.get();
}
}
public class ThreadLocalTest extends Thread {
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("Thread[" + Thread.currentThread().getName() + "],counter=" + Counter.getNextCounter());
}
}
}
public class Test {
public static void main(String[] args) throws Exception {
ThreadLocalTest testThread1 = new ThreadLocalTest();
ThreadLocalTest testThread2 = new ThreadLocalTest();
ThreadLocalTest testThread3 = new ThreadLocalTest();
testThread1.start();
testThread2.start();
testThread3.start();
}
}

我们来运行一下上面的代码,并看看输出结果:

Thread[Thread-2],counter=11
Thread[Thread-2],counter=12
Thread[Thread-2],counter=13
Thread[Thread-0],counter=11
Thread[Thread-0],counter=12
Thread[Thread-0],counter=13
Thread[Thread-1],counter=11
Thread[Thread-1],counter=12
Thread[Thread-1],counter=13

ThreadLocal模式最合适的使用场景:在同一个线程的不同开发层次中共享数据。

ThreadLocal模式的两个主要步骤:

  • 建立一个类,并在其中封装一个静态的ThreadLocal变量,使其成为一个共享数据环境。
  • 在类中实现访问静态ThreadLocal变量的静态方法(设值和取值)。

未完待续...

ThreadLocal模式的原理的更多相关文章

  1. 【Java EE 学习 19】【使用过滤器实现全站压缩】【使用ThreadLocal模式解决跨DAO事务回滚问题】

    一.使用过滤器实现全站压缩 1.目标:对网站的所有JSP页面进行页面压缩,减少用户流量的使用.但是对图片和视频不进行压缩,因为图片和视频的压缩率很小,而且处理所需要的服务器资源很大. 2.实现原理: ...

  2. 浅谈ThreadLocal模式

    一.前言: ThreadLocal模式,严格意义上不是一种设计模式,而是java中解决多线程数据共享问题的一个方案.ThreadLocal类是java JDK中提供的一个类,用来解决线程安全问题,并不 ...

  3. ThreadLocal模式的核心元素

    首先来看ThreadLocal模式的实现机理:在JDK的早期版本中,提供了一种解决多线程并发问题的方案:java.lang.ThreadLocal类.ThreadLocal类在维护变量时,世纪使用了当 ...

  4. 【深入比较ThreadLocal模式与synchronized关键字】

    [深入比较ThreadLocal模式与synchronized关键字]ThreadLocal模式与synchronized关键字都是用于处理多线程并发访问变量的问题.只是两者处理问题的角度和思路不同. ...

  5. ThreadLocal模式探索

    一.首先,ThreadLocal模式使共享数据能多个线程被访问,每个线程访问的只是这个数据的副本,线程之间互不影响. 例子1: package Thread2; public class Counte ...

  6. HA主备路由模式的原理 + HA和负载均衡的区别

       HA主备路由模式的原理 HA是High Availability缩写,即高可用性 ,可防止网络中由于单个防火墙的设备故障或网络故障导致网络中断,保证网络服务的连续性和安全强度.目前,ha功能已经 ...

  7. MVC模式的原理

    说说MVC模式的原理,Android SDK 中有哪些组件使用到了MVC模式,其基本原理是什么?[国内某著名软件外包公司 2010 年面试题] 答案:MVC 的基本原理就是通过Controller 连 ...

  8. 源码|ThreadLocal的实现原理

    ThreadLocal也叫"线程本地变量"."线程局部变量": 其作用域覆盖线程,而不是某个具体任务: 其"自然"的生命周期与线程的生命周期 ...

  9. ThreadLocal模式与synchronized关键字的比较

    ThreadLocal模式与synchronized关键字都是用于处理多线程并发访问变量的问题.只是两者处理问题的角度和思路不同. 1)ThreadLocal是一个Java类,通过对当前线程(Thre ...

随机推荐

  1. spell checking

    Spell checker Description You, as a member of a development team for a new spell checking program, a ...

  2. kafka 集群部署 多机多broker模式

    kafka 集群部署 多机多broker模式 环境IP : 172.16.1.35   zookeeper   kafka 172.16.1.36   zookeeper   kafka 172.16 ...

  3. artTemplate-3.0(与项目实际结合)

    引入artTemplate.js <script type="text/javascript" src="${ctx}/assets/plugins/artTemp ...

  4. CDOJ 1268 Open the lightings

    组合数学题.好难啊,请教了Xiang578大神&&看了他题解才会的...... 甩上题解链接:http://blog.csdn.net/xinag578/article/details ...

  5. HUST 1602 Substring

    水题. #include<cstdio> #include<cstring> #include<cmath> #include<string> #inc ...

  6. 02 easyui -parser

    parser: 页面节点class=“easyui-” +组件名(在plugins里) ,则可以 渲染成相应的组件.

  7. wdcp使用记录--持续更新

    1.扩展memcache 资源地址:http://download.csdn.net/detail/fangminglei/8390449 上传至root文件夹运行 注:官方提供的文件不能安装,需要改 ...

  8. List<KeyValuePair<TKey,TValue>> 与 Dictionary<TKey,TValue> 不同

    两者都可以通过 KeyValuePair<TKey,TValue> 进行遍历,并且两者可以相互转换: List<KeyValuePair<string,string>&g ...

  9. 微软2017MVP大礼包拆箱攻略

    容我本周偷个懒,晒个大礼包就糊弄过去了.13号晚上拿到的大礼包,激动的没敢拆,一直等到娃睡着了,才偷偷打开了快递,忍了两天没忍住,上来晒图得瑟一下,请各位轻拍,谢谢! 1.大礼包的盒子(联邦快递的盒子 ...

  10. JavaWeb:EL & JSTL

    EL:全名为 Expression Language 1.语法:${sessionScope.user.sex}(从Session 的范围中,取得用户的性别), 所有的EL 都是以 ${  为起始,以 ...