还记得Java并发最佳实践有一条提到尽量不要在线程间共享状态。但我们在实现一个thread或者runnable接口的时候很容易放这个错误,导致一些诡异的问题。
让我们看下面这个例子:
public class UnsafeTask implements Runnable { private Date startDate; @Override public void run() { startDate = new Date(); System.out.printf("Starting Thread: %s : %s\n", Thread.currentThread() .getId(), startDate); try { TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10)); } catch (InterruptedException e) { e.printStackTrace(); } System.out.printf("Thread Finished: %s : %s\n", Thread.currentThread() .getId(), startDate); } } public class Core { public static void main(String[] args) { UnsafeTask task = new UnsafeTask(); for (int i = 0; i < 10; i++) { Thread thread = new Thread(task); thread.start(); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } } } }
我们看到如下输出:
Starting Thread: 9 : Thu Feb 27 17:26:34 CST 2014 Starting Thread: 10 : Thu Feb 27 17:26:36 CST 2014 Starting Thread: 11 : Thu Feb 27 17:26:38 CST 2014 Starting Thread: 12 : Thu Feb 27 17:26:40 CST 2014 Thread Finished: 11 : Thu Feb 27 17:26:40 CST 2014
结束的线程显示的日期与刚启动的线程的日期是一样的,原因处在我们在同一个Runnable实例上启动了多个线程,而startDate域是多个线程之间共享的。
怎样避免这个问题呢? 一种方法是让一个线程对应一个Runnable实例,还有一种更有效的方法就是用Java Concurrency API提供的ThreadLocal变量,这种方法可以避免创建过多的Runnable实例。
看如下代码:
import java.util.Date; import java.util.concurrent.TimeUnit; public class SafeTask implements Runnable { private static ThreadLocal<Date> startDate = new ThreadLocal<Date>() { protected Date initialValue(){ return new Date(); } }; @Override public void run() { System.out.printf("Starting Thread: %s : %s\n",Thread. currentThread().getId(),startDate.get()); try { TimeUnit.SECONDS.sleep((int)Math.rint(Math.random()*10)); } catch (InterruptedException e) { e.printStackTrace(); } System.out.printf("Thread Finished: %s : %s\n",Thread. currentThread().getId(),startDate.get()); } }
仔细查看源代码,ThreadLocal实现中有一个TheadLocalMap(开地址哈希?),存放各个Thread和值对应的值域,map的key是用线程和它的域的组合算出来的,这样每个线程就不共享状态了。
初次之外,还可以调用ThreadLocal的get(),set()方法去获得,更新自己的状态。
JDK还提供了一个更复杂的InheritableThreadLocal类,如果A线程创建了B线程,给类可以帮助B从A中获取相关状态域的一份拷贝。
相关推荐
ThreadLocal保证一个类的实例变量在各个线程中都有一份单独的拷贝, 从而不会影响其他线程中的实例变量
java8-threadlocal 模拟事务的行为,以了解我们如何使用ThreadLocal来提供帮助。
Java资料—详解ThreadLocal ;Java资料—详解ThreadLocal ;Java资料—详解ThreadLocal ;Java资料—详解ThreadLocal Java资料—详解ThreadLocal
Java 线程本地分析器。 用于 Java 应用程序分析的小库。 收集同一伞下的所有性能指标,可以一起打印出来。 最好与一些日志框架一起使用。 核心没有任何依赖关系,因此可以位于类加载器层次结构的顶部。 支持 Spring...
本地学习练习demo的eclipse工作空间:主要包括多线程的相关demo以及quartz调度的简单实现和其他java基础的demo练习
java 简单的ThreadLocal示例
NULL 博文链接:https://yizhenn.iteye.com/blog/2293339
在学习Java过程中,自己收集了很多的Java的学习资料,分享给大家,有需要的欢迎下载,希望对大家有用,一起学习,一起进步。
JAVA并发-自问自答学ThreadLocal
详解java底层实现原理,ThreadLocal底层实现的数据结构,为什么不会导致内存泄露
java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多...
Java中ThreadLocal的设计与使用.doc
第1章 Java概述 1 1.1 Java语言的发展简史 2 1.2 Java的竞争对手及各自优势 4 1.2.1 C#简介和优势 4 1.2.2 Ruby简介和优势 4 1.2.3 Python的简介和优势 5 1.3 Java程序运行机制 5 1.3.1 高级语言的运行机制 6...
Java大师级源码 Java8-Source-Code 简介 Java8源码学习 ├── ...├── java/(常用代码都在此文件夹下) ...ThreadLocal ├── javax/ ├── launcher/ ├── org/ 技术交流 博客地址: QQ群:专注的程序猿 282087535
主要介绍了彻底理解Java 中的ThreadLocal的相关资料,需要的朋友可以参考下
ThreadLocal翻译成中文比较准确的叫法应该是:线程局部变量。使用这个工具类可以很简洁地编写出优美的多线程程序。接下来通过本文给大家介绍Java中的ThreadLocal,需要的朋友可以参考下
今天小编就为大家分享一篇关于Java源码解析ThreadLocal及使用场景,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
NULL 博文链接:https://justsee.iteye.com/blog/791919
Template模板设计模式改造threadlocal控制事务
java8 源码 Java8-Source-Code 简介 Java8源码学习 ...ThreadLocal ├── javax/ ├── launcher/ ├── org/ To do a test for git cherry-pick. 技术交流 博客地址: QQ群:专注的程序猿 282087535