共享数据是多线程应用最常见的问题之一,但有时我们需要为每个线程保存一份独立的变量。Java API 提供了 ThreadLocal 来解决这个问题。

一个 ThreadLocal 作用的例子:

import java.util.Date;

public class Main {

    public static void main(String[] args) {
Runnable task = new Runnable() { private ThreadLocal<Date> dateVar = new ThreadLocal<Date>(); public void run() {
dateVar.set(new Date());
System.out.printf("%s, GET dateVar: %s\n", Thread.currentThread().getName(), dateVar.get());
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("%s, FINAL dateVar: %s\n", Thread.currentThread().getName(), dateVar.get());
}
}; for (int i = 0; i < 3; i++) {
String threadName = "Thread" + (i + 1);
Thread thread = new Thread(task, threadName);
thread.start();
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
} }
}

观察运行结果:

Thread1, GET dateVar: Fri Oct 14 22:06:33 CST 2016
Thread2, GET dateVar: Fri Oct 14 22:06:34 CST 2016
Thread3, GET dateVar: Fri Oct 14 22:06:35 CST 2016
Thread1, FINAL dateVar: Fri Oct 14 22:06:33 CST 2016
Thread2, FINAL dateVar: Fri Oct 14 22:06:34 CST 2016
Thread3, FINAL dateVar: Fri Oct 14 22:06:35 CST 2016

可以看到每个线程都共用一个 Task 实例,线程之间间隔 1 秒启动。线程执行 run 方法的时候首先将 dateVar 的值设置为系统当前时间并打印 dateVar 值,然后线程会休眠 5 秒,最后再打印 dateVar 的值。注意到 run 方法设置 dateVar 值与最后打印 dateVar 值间隔 5 秒,而下一个线程启动时只间隔 1 秒,在当前线程打印 dateVar 之前,下个线程甚至是下下个线程已经重置 dateVar 的值,但是每个线程最后打印 dateVar 值的时候仍然是显示该线程最初设置的值。可见,每个线程中的 dateVar 并不会被其他线程所干扰。

再看下没有使用 ThreadLocal 的情况。

import java.util.Date;

public class Main2 {

    public static void main(String[] args) {
Runnable task = new Runnable() { private Date dateVar; public void run() {
dateVar = new Date();
System.out.printf("%s, GET dateVar: %s\n", Thread.currentThread().getName(), dateVar);
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("%s, FINAL dateVar: %s\n", Thread.currentThread().getName(), dateVar);
}
}; for (int i = 0; i < 3; i++) {
String threadName = "Thread" + (i + 1);
Thread thread = new Thread(task, threadName);
thread.start();
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

观察运行结果:

Thread1, GET dateVar: Fri Oct 14 22:17:38 CST 2016
Thread2, GET dateVar: Fri Oct 14 22:17:39 CST 2016
Thread3, GET dateVar: Fri Oct 14 22:17:40 CST 2016
Thread1, FINAL dateVar: Fri Oct 14 22:17:40 CST 2016
Thread2, FINAL dateVar: Fri Oct 14 22:17:40 CST 2016
Thread3, FINAL dateVar: Fri Oct 14 22:17:40 CST 2016

可以看到,当直接使用 Date 类型时,线程最终打印 dateVar 的值与最初设置的值不一致。这个是因为所有的线程共享一个 Task 实例,所以 dateVar 是共享数据,在多个线程竞争时,造成数据不一致。

ThreadLocal 的初始化

可以通过覆盖 initialValue 方法初始化 ThreadLocal 的值。

ThreadLocal<Date> dateVar = new ThreadLocal<Date>() {
@Override
protected Date initialValue() {
return new Date();
}
};

InheritableThreadLocal

如果在线程 A 创建了子线程 B,那么线程 A 和 线程 B 都是各自维护一份 ThreadLocal 值,线程 A 的 ThreadLocal 值不会传递给子线程 B。Java API 提供了 InheritableThreadLocal 类,它是 ThreadLocal 的子类。如果使用 InheritableThreadLocal,那么在线程 A 创建子线程 B,线程 A 和 线程 B 仍然都是各自维护一份 InheritableThreadLocal 值,但是现场 A 的 InheritableThreadLocal 值则会传递给子线程 B。可以重写 childValue 方法修改从父现场继承的 InheritableThreadLocal 值。

public static ThreadLocal<Date> dateVar = new InheritableThreadLocal<Date>() {
protected Date initialValue() {
return new Date();
}; protected Date childValue(Date parentValue) {
return new Date(parentValue.getTime() + 1000L);
};
};

Java Concurrency - ThreadLocal, 本地线程变量的更多相关文章

  1. Java 类 ThreadLocal 本地线程变量

    前言:工作中将要使用ThreadLocal,先学习总结一波.有不对的地方欢迎评论指出. 定义 ThreadLocal并不是一个Thread,而是Thread的局部变量.这些变量不同于它们的普通对应物, ...

  2. ThreadLocal本地线程变量的理解

     一般的Web应用划分为展现层.服务层和持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用.在一般情况下,从接收请求到返回响应所经过的所有程序调用都同属于一个线程.       ...

  3. ThreadLocal = 本地线程?

    一.定义 ThreadLocal是JDK包提供的,从名字来看,ThreadLocal意思就是本地线程的意思. 1.1 是什么? 要想知道他是个啥,我们看看ThreadLocal的源码(基于JDK 1. ...

  4. Flask中的ThreadLocal本地线程,上下文管理

    先说一下和flask没有关系的: 我们都知道线程是由进程创建出来的,CPU实际执行的也是线程,那么线程其实是没有自己独有的内存空间的,所有的线程共享进程的资源和空间,共享就会有冲突,对于多线程对同一块 ...

  5. java concurrency: ThreadLocal及其实现机制

    转载:http://shmilyaw-hotmail-com.iteye.com/blog/1703382 ThreadLocal概念 从字面上来理解ThreadLocal,感觉就是相当于线程本地的. ...

  6. Java线程变量问题-ThreadLocal

    关于Java线程问题,在博客上看到一篇文章挺好的: https://blog.csdn.net/w172087242/article/details/83375022#23_ThreadLocal_1 ...

  7. 深入研究java.lang.ThreadLocal类(转)

    引用:http://lavasoft.blog.51cto.com/62575/51926/ 一.概述   ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并 ...

  8. 深入研究java.lang.ThreadLocal类

        一.概述   ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是threadlocalvariable(线程局部变量).也许 ...

  9. Java多线程——ThreadLocal类

    一.概述   ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是threadlocalvariable(线程局部变量).也许把它命名 ...

随机推荐

  1. Javascript/Jquery——简单定时器的多种实现方法

    第一种方法: <script language="javascript"> //使用setInterval间歇调用 (不建议使用该方法) $(function(){ s ...

  2. jq倒计时

    var referTime = 5; setInterval("refer('#show')", 1000); function refer(obj){ if(referTime ...

  3. c++回调函数

    dcc组件支持回调函数接口,当连接/断开连接对端时,调用传入的函数指针. A库和B库想做到不耦合,但是A库需要用到B库的某些函数,A库提供回调函数接口,在初始化的时候指定回调函数,降低耦合程度,每一个 ...

  4. Host key verification failed解决

    SSH 登录失败:Host key verification failed 由于公钥不一样了,所以无法登录,提示信息是 KEY 验证失败. 解决方法是: 在 /root/.ssh/known_host ...

  5. Ehcache(01)——简介、基本操作

    http://haohaoxuexi.iteye.com/blog/2112170 目录 1       CacheManager 1.1      构造方法构建 1.2      静态方法构建 2  ...

  6. 教你50招提升ASP.NET性能(二十三):StringBuilder不适用于所有字符串连接的场景;String.Join可能是

    (41)StringBuilder is NOT the answer for all string concatenation scenarios; String.Join could be 招数4 ...

  7. mvc4 web-api 与unity搭建接口

    对于接口重要的应该是 功能实现,合法性验证,性能监控,日志等模块 通过unity aop功能可以实现统一的日志模块和性能监控. 1.新建mvc4 webapi项目 nuget添加 unity 3.0+ ...

  8. c# socket通信较完善方案

    c#的socket通信应用.文件较多.附件为工程.  core   AbstractBytesWorker.cs    字节工作器(基类),用于用于同一不同功能的字节工作器  BinaryHand.c ...

  9. MHA手动切换 原创2 (主参与复制)

    monitor 执行下面命令后, --orig_master_is_new_slave :原主变为新从,即server1变成了slave1的从.slave2跟据app1.conf中配制也变成了slav ...

  10. Word2010 清除样式

    使用场景         有时候我们在网页上面粘贴一些精华文章或者从去整理别人已经完成的word的时候,会发现它自带的格式,可能并不是我们所理想的格式,所以此时就不得不去重新编辑其格式,但是word里 ...