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则用于线程间的数据隔离。
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则用于线程间的数据隔离。
相关文章推荐
- 彻底理解ThreadLocal
- ThreadLocal的理解
- ThreadLocal理解
- 深入理解ThreadLocal
- 正确理解ThreadLocal帮助我们更好的理解Spring,Hibernate
- ThreadLocal 的理解
- ThreadLocal的理解
- 正确理解ThreadLocal
- ThreadLocal简单理解
- ThreadLocal的理解
- ThreadLocal的个人理解
- 彻底理解ThreadLocal
- 正确理解ThreadLocal
- 深入理解ThreadLocal
- ThreadLocal的一些理解
- 理解java中的ThreadLocal
- Android -理解ThreadLocal、Looper
- 正确理解ThreadLocal
- 正确理解ThreadLocal
- ThreadLocal模式的一点小理解