您的位置:首页 > 其它

ThreadLocal的理解

2014-07-26 23:03 316 查看
很多人对ThreadLocal和Thread这两个之间关系不理解,先看Thread的源码,这样才可以了解到Thread和ThreadLocal的内在联系,查看源码发现每个线程都有ThreadLocal的内部类ThreadLocalMap类的变量 ,线程源码片段:

public class Thread implements Runnable {

ThreadLocal.ThreadLocalMap threadLocals = null;

.......

}

查看ThreadLocal的set方法看做了那些操作

public class ThreadLocal<T> {

public void set(T value) {

Thread t = Thread.currentThread();

ThreadLocalMap map = getMap(t);

if (map != null)

map.set(this, value);

else

createMap(t, value);

}

.......

}

会发现1:找到当前线程对象,2该线程对象中取出ThreadLocalMap,如果当前线程有ThreadLocalMap,就把值放入该线程的ThreadLocalMap中,key是ThreadLocal,value是放入的值,如果没有则创建一个新的ThreadLocalMap对象,记住ThreadLocal通过set方法设置值时,是把该值存在当前线程的Thread的ThreadLocalMap中,与当前线程有关,到这里可能会有人不解,ThreadLocalMap是啥,是如何保存value值的,再次查看源码:

public class ThreadLocal<T> {

static class ThreadLocalMap {

static class Entry extends WeakReference<ThreadLocal> {

Object value;

Entry(ThreadLocal k, Object v) {

super(k);

value = v;

}

}

private Entry[] table;

ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {

table = new Entry[INITIAL_CAPACITY];

int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);

table[i] = new Entry(firstKey, firstValue);

size = 1;

setThreshold(INITIAL_CAPACITY);

}

}

}

通过源码可以发现,ThreadLocalMap是ThreadLocal的一个静态内部类,该内部类里面有一个Entry类型的table的数组,通过ThreadLocal来得到该数组的下标值,然后在该位置上引用到new出来的Entry对象,简单的说就是Thread对象有一个ThreadLocalMap对象属性,该属性对象里面会有一个数组,这个数组存储的就是value值,当然是封装以后的value值。

public class ThreadLocal<T> {

public T get() {

Thread t = Thread.currentThread();

ThreadLocalMap map = getMap(t);

if (map != null) {

ThreadLocalMap.Entry e = map.getEntry(this);

if (e != null)

return (T)e.value;

}

return setInitialValue();

}

public void set(T value) {

Thread t = Thread.currentThread();

ThreadLocalMap map = getMap(t);

if (map != null)

map.set(this, value);

else

createMap(t, value);

}

}

通过阅读上面代码,可以发现,ThreadLocal的get方法是找到当前线程,取得该线程对象的ThreadLocalMap属性对象,通过ThreadLocal作为key值而得到的value值,set方法亦如此。原来value值是存在每个线程对象的ThreadLocalMap属性中,ThreadLocal是作为该“map"的 key值,通过key就可以把每个线程对象存储的value值出来了。这样会发现如果在线程里面存储的是方法的局部变量的话,那么每个线程可以自由的改变value值,而对其他线程的value没影响,如果所有线程共用value对象,则线程改变value,同时也改变了其他线程的value值,

例子:

package com.threadlocal.test;

import itcast.io.Student;

import java.util.Random;

public class ThreadLocalTest2 implements Runnable{

private ThreadLocal threadLocal=new ThreadLocal();

private static Random random=new Random();

private Student s=new Student();

private static int i=1;

@Override

public void run() {

print();

}

public void print( ){

Thread thread=Thread.currentThread();

System.out.println(thread.getName()+"线程进入");

// Student s=(Student)threadLocal.get();

s.setAge(i++);

s.setName("name"+i);

threadLocal.set(s);

//System.out.println(thread.getName()+" "+s.hashCode()+" "+s.getName()+" "+s.getAge() );

System.out.println(thread.getName()+"线程第一次取得值是 "+((Student)threadLocal.get()).hashCode()+" "+((Student)threadLocal.get()).getName()+" "+((Student)threadLocal.get()).getAge());

try {

Thread.sleep(3000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(thread.getName()+"线程第二次取得值是 "+((Student)threadLocal.get()).hashCode()+" "+((Student)threadLocal.get()).getName()+" "+((Student)threadLocal.get()).getAge());

System.out.println(thread.getName()+"线程退出");

}

/**

* @param args

*/

public static void main(String[] args) {

ThreadLocalTest2 threadLocal=new ThreadLocalTest2();

Thread t1=new Thread(threadLocal,"t1");

Thread t2=new Thread(threadLocal,"t2");

t1.start();

t2.start();

}

}

打印出:

t1线程进入

t2线程进入

t1线程第一次取得值是 4872882 name2 1

t2线程第一次取得值是 4872882 name3 2

t2线程第二次取得值是 4872882 name3 2

t2线程退出

t1线程第二次取得值是 4872882 name3 2

t1线程退出

看出如果多个线程共享一个对象,改变t2线程中value对象,会同时改变t1线程value对象值.如果把print方法改为如下:

public void print( ){

Thread thread=Thread.currentThread();

System.out.println(thread.getName()+"线程进入");

Student s=new Student();

s.setAge(i++);

s.setName("name"+i);

threadLocal.set(s);

//System.out.println(thread.getName()+" "+s.hashCode()+" "+s.getName()+" "+s.getAge() );

System.out.println(thread.getName()+"线程第一次取得值是 "+((Student)threadLocal.get()).hashCode()+" "+((Student)threadLocal.get()).getName()+" "+((Student)threadLocal.get()).getAge());

try {

Thread.sleep(3000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(thread.getName()+"线程第二次取得值是 "+((Student)threadLocal.get()).hashCode()+" "+((Student)threadLocal.get()).getName()+" "+((Student)threadLocal.get()).getAge());

System.out.println(thread.getName()+"线程退出");

}

打印出:

t1线程进入

t2线程进入

t2线程第一次取得值是 4872882 name3 2

t1线程第一次取得值是 25724761 name3 1

t1线程第二次取得值是 25724761 name3 1

t1线程退出

t2线程第二次取得值是 4872882 name3 2

t2线程退出

可以看出多个线程如果不共享一个对象,每个线程一个对象的话,一个线程改变value,是不会影响到其他线程的value值,所以ThreadLocal是不能解决多个线程共享一个对象的问题的。ThreadLocal使用场合主要解决多线程中数据数据因并发产生不一致问题,ThreadLocal为每个线程的中并发访问的数据提供一个副本,通过访问副本来运行业务,这样的结果是耗费了内存,单大大减少了线程同步所带来性能消耗,也减少了线程并发控制的复杂度。

ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区别。synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。

Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: