首先来看一下多线程问题:
class Test{
private static Integer data = 0;//使用Integer普通变量保存数据
public static void set(Integer num){
data = num;
}
public static Integer get(){
System.out.println("num ==> " +data);
return data;
}
}
public class CopyOfMainTest{
public static void main(String[] args){
for(int i=0;i<10;i++){
final int num = i;
new Thread(){
public void run(){
Test.set(num);
Test.get();
}
}.start();
}
}
}
执行的结果如下:
num ==> 4
num ==> 0
num ==> 4
num ==> 4
num ==> 3
num ==> 5
num ==> 7
num ==> 9
num ==> 9
num ==> 8
分析:可以看到以下多个线程中data的值时一样的,使得数据结果很混乱,按照正常的思维模式,每一个线程应该得到一个数据,从0到9,这就是我们经常说的线程安全问题,这里肯定是不安全的。当然了你可以用synchronized去解决这个问题,但是多线程的并发处理的效率就会大大折扣。
来看一个用ThreadLocal存放变量的例子:
//使用ThreadLocal保存数据
class Test{
public static ThreadLocal<Integer> local = new ThreadLocal<Integer>();
public static void set(Integer num){
local.set(num);
}
public static Integer get(){
System.out.println("num ==> " + local.get());
return local.get();
}
}
public class CopyOfMainTest{
public static void main(String[] args){
for(int i=0;i<10;i++){
final int num = i;
new Thread(){
public void run(){
Test.set(num);
Test.get();
}
}.start();
}
}
}
执行的结果如下:
num ==> 0
num ==> 1
num ==> 2
num ==> 3
num ==> 6
num ==> 5
num ==> 8
num ==> 4
num ==> 9
num ==> 7
只是顺序上变了而已,每个线程都得到自己应该得到的Integer变量值。
分析原因:
通过对java API的查阅,发现Tread 和ThreadLocal都在java.lang包下。在java.lang.Thread源码中,有这样一段代码。这个threadLocals就是那些设计者们已经为我们考虑了在线程中存放变量的问题。
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
在看ThreadLocal源码:
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
public T get() {//get方法
Thread t = Thread.currentThread();//获取当前上下文线程(哪个线程在调用该方法)
ThreadLocalMap map = getMap(t);//把当前线程作为参数丢给getMap方法,返回一个ThreadLocal的内置对象ThreadLocalMap (map)
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);//取出当前上下ThreadLocal对象对应的值 Entry getEntry(ThreadLocal key) if (e != null) return (T)e.value; } return setInitialValue();
}
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {//set方法
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);//把当前的ThreadLocal对象作为key把值存入,set(ThreadLocal key, Object value)
else
createMap(t, value);
}
/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
通过对源码的分析发现,ThreadLocal在存变量的时候是根据当前上下文线程 和 当前ThreadLocal对象存入数值的。所以当调用get方法时,根据当前上下文线程和ThreadLocal对象就取出了自己唯一的那个值,而不会取到别的线程的值。
强烈推荐两篇文章:
http://qiankunli.github.io/2014/09/02/ThreadLocal.html
http://woshixy.blog.51cto.com/5637578/1275284
注册没几天,就写了一篇,写的不好,请大家提出意见,慢慢改进。