还记得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中获取相关状态域的一份拷贝。
分享到:
相关推荐
java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多...
java 简单的ThreadLocal示例
ThreadLocal保证一个类的实例变量在各个线程中都有一份单独的拷贝, 从而不会影响其他线程中的实例变量
简单分析Java线程编程中ThreadLocal类的使用共4页.pdf.zip
Java线程:概念与原理 Java线程:创建与启动 Java线程:线程状态的转换 ...Java线程:深入ThreadLocal 一、标准例子 二、不用ThreadLocal 三、自己实现个ThreadLocal 四、透过现象看本质 Java线程:大总结
详解java底层实现原理,ThreadLocal底层实现的数据结构,为什么不会导致内存泄露
synchronized关键字不属于方法特征签名的一部分,所以可以在覆盖方法的时候加上去。也就是说,在父类的方法声明上可以没有synchronized关键字,而在子类覆盖该方法时加上synchronized关键字。 注意:使用...
主要介绍了Java单线程ThreadLocal串值问题解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
主要介绍了Java多线程编程之ThreadLocal线程范围内的共享变量,本文讲解了ThreadLocal的作用和目的、ThreadLocal的应用场景、ThreadLocal的使用实例等,需要的朋友可以参考下
Java中ThreadLocal的设计与使用.doc
早在 JDK 1.2 的时代,java.lang.ThreadLocal 就诞生了,它是为了解决多线程并发问题而设计的,只不过设计得有些难用,所以至今没有得到广泛使用。其实它还是挺有用的,不相信的话,我们一起来看看这个例子吧。 一个...
Java资料—详解ThreadLocal ;Java资料—详解ThreadLocal ;Java资料—详解ThreadLocal ;Java资料—详解ThreadLocal Java资料—详解ThreadLocal
关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. ...
java 多线程 ThreadLocal
ThreadLocal则为每一个线程提供了一个变量副本,从而隔离了多个线程访问数据的冲突,ThreadLocal提供了线程安全的对象封装,下面我们就来详细了解一下吧
本例以序列号生成的程序为例,展示ThreadLocal的使用
主要介绍了Java线程编程中ThreadLocal类的使用,包括使用其对共享变量的操作的分析,需要的朋友可以参考下
主要介绍了彻底理解Java 中的ThreadLocal的相关资料,需要的朋友可以参考下
ThreadLocal翻译成中文比较准确的叫法应该是:线程局部变量。使用这个工具类可以很简洁地编写出优美的多线程程序。接下来通过本文给大家介绍Java中的ThreadLocal,需要的朋友可以参考下
Java并发编实践之ThreadLocal变量.doc