在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. Mysql基本操作整理

    1.登录数据库 格式: mysql -h主机地址 -u用户名 -p用户密码 –P端口 –D数据库 –e “SQL 内容”>mysql -uroot -p 数据库名称~~~~~~~~~~~~~~~ ...

  2. ASP.NET AJAX注册命名空间

    AJAX库支持命名空间的概念. 类可以分组到不同的命名空间.AJAX库会自动创建在此层次中不存在的任何级别的命名空间. <%@ Page Language="C#" Auto ...

  3. JSON的BUG

    问题描述: method_cfg["input_method"][0].asString() 报错误: test1.cpp:40:46: error: ambiguous over ...

  4. iOS开发——离线缓存

    先搭好架子,有时间了再填充.

  5. iOS给自定义个model排序

    今天有朋友问我怎么给Model排序,我顺便写了一个,伸手党直接复制吧. 例如,我建了一个Person类,要按Person的年龄属性排序: Person *per = [[Person alloc] i ...

  6. webstorm安装express报错

    .... Exit code: -1 解决方法: webstorm创建express 需要 预先安装express-generator npm install express-generator -g

  7. Javascript 页面刷新

    Javascript 页面刷新的实现代码收藏 1 2 3 4 5 6 7 8 history.go(0) location.reload() location=location location.as ...

  8. 在 WindowMobile 上的模拟LED 显示屏插件(转)

    源:在 WindowMobile 上的模拟LED 显示屏插件 我在给一个对话框上的控件查找翻看合适的图标时,无形中看到了一个LED显示屏的图标,这里所说的LED显示屏是指由很多LED灯密集排列组成的点 ...

  9. MVP学习笔记——参考Google官方demo

    demo地址:https://github.com/googlesamples/android-architecture 在这个项目里,每个包的分工都很明确,大体上来说,一个包会对应一个界面.一个界面 ...

  10. CodeSmith生成实体类

    1.新建一个模板,将以下代码复制进去,在属性工具栏里设置 SourceTtable和NameSpace , 点击生成即可 <%@ CodeTemplate Language="C#&q ...