共享数据是多线程应用最常见的问题之一,但有时我们需要为每个线程保存一份独立的变量。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. POJ 1751 Highways (kruskal)

    题目链接:http://poj.org/problem?id=1751 题意是给你n个点的坐标,然后给你m对点是已经相连的,问你还需要连接哪几对点,使这个图为最小生成树. 这里用kruskal不会超时 ...

  2. UVaLive 7370 Classy (排序,比较)

    题意:给定 n 个人,和他们的数进行比较,从后面开始比,如果不够长,加middle接着比,直到没有,如果还相同比名字. 析:很水的题,就不用说了. 代码如下: #pragma comment(link ...

  3. UML学习(二)-----类图

    UML学习(二)-----类图 http://www.cnblogs.com/silent2012/archive/2011/09/07/2169946.html http://www.cnblogs ...

  4. django控制admin的model显示列表

    class goods(models.Model):    name = models.CharField(max_length=300)    price = models.IntegerField ...

  5. MFC拆分窗口及它们之间的数据交换(转)

    转自:http://blog.csdn.net/nuptboyzhb/article/details/7455471 源代码:http://download.csdn.net/detail/nuptb ...

  6. ckeditor内容保存后显示问题

    ckeditor保存到数据库中的数据是html格式,如果要在前台进行显示,需要采用${news.content },这样数据才能进行正常的显示,否则如果采用<s:property value=& ...

  7. HTML5 - HTML5 postMessage API 注意事项

    一:发送 window.postMessage("Hello, world", "http://127.0.0.1:8080"); 注意,必须要加上http:/ ...

  8. python flask 部署

    flask在开发的时候,经常启动本身进行调试(本身可以设置监听的端口,例如 在app.run(port=8088),当然默认不设置端口为5000). 但生产环境经常使用uswgi充当flask的宿主, ...

  9. delphi 设置超链接

     的属性 的事件 的方法 //1设置链接类型//2获取样式 链接和提示信息 //title是提示信息//HTTPS https://<a href="https://www.baidu ...

  10. ZJU-PAT 1065. A+B and C (64bit) (20)

    Given three integers A, B and C in [-263, 263], you are supposed to tell whether A+B > C. Input S ...