ThreadLocal与InheritableThreadLocal的使用
2015-12-29 14:14
357 查看
ThreadLocal,线程本地存储,为变量在每个线程中都创建了一个副本,那么每个线程可以独立地改变和访问自己的副本变量,而不会影响其它线程所对应的副本变量。从线程的角度看,目标变量就像是线程的本地变量,这也是类名中“Local”所要表达的意思。
ThreadLocal不是用来解决对象共享访问问题的,而是提供了保持对象的方法和避免参数传递的对象访问方式。每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象。
在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个 threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本。初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对 Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为
value,存到threadLocals。然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。
ThreadLocal类的定义:public class ThreadLocal<T>
get()方法是用来获取ThreadLocal在当前线程中保存的变量副本
这里的getMap,是调用当期线程t,返回当前线程t中的一个成员变量threadLocals。
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
在Thread类中会发现有threadLocals与inheritableThreadLocals两个成员变量,都是ThreadLocal.ThreadLocalMap类。
再看setInitialValue方法:
set方法就比较简单了:
简单示例:
InheritableThreadLocal类继承于ThreadLocal类,所以它具有ThreadLocal类的特性,但又是一种特殊的 ThreadLocal,其特殊性在于InheritableThreadLocal变量值会自动传递给所有子线程,即在创建子线程时,子线程会接收所有可继承的线程局部变量的初始值,以获得父线程所具有的值。而普通ThreadLocal变量不行。
如果一个子线程调用InheritableThreadLocal的get(),那么它将与它的父线程看到同一个对象。为保护线程安全性,应该只对不可变对象(一旦创建,其状态就永远不会被改变的对象)使用InheritableThreadLocal,因为对象被多个线程共享。
简单示例:
public class InheritableThreadLocalTest {
InheritableThreadLocal<StringBuilder> a = new InheritableThreadLocal<>();
InheritableThreadLocal<String> b = new InheritableThreadLocal<String>();
public static void main(String[] args) throws InterruptedException {
final InheritableThreadLocalTest test = new InheritableThreadLocalTest();
test.a.set(new StringBuilder("main---hello"));
test.b.set("main---zero");
System.out.println(test.a.get());
System.out.println(test.b.get());
Thread thread1 = new Thread() {
public void run() {
System.out.println(test.a.get());
System.out.println(test.b.get());
StringBuilder a = test.a.get().append("---subThread");
test.a.set(a);
test.b.set("subThread--zero");
System.out.println(test.a.get());
System.out.println(test.b.get());
};
};
thread1.start();
thread1.join();
System.out.println(test.a.get());
System.out.println(test.b.get());
}
}运行结果:
ThreadLocal不是用来解决对象共享访问问题的,而是提供了保持对象的方法和避免参数传递的对象访问方式。每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象。
在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个 threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本。初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对 Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为
value,存到threadLocals。然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。
ThreadLocal类的定义:public class ThreadLocal<T>
get()方法是用来获取ThreadLocal在当前线程中保存的变量副本
public T get() { Thread t = Thread.currentThread();//取得当前线程 ThreadLocalMap map = getMap(t);// 获取该线程对应的ThreadLocalMap if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this);//获取到<key,value>键值对,注意这里获取键值对传进去的是this,而不是当前线程t。 if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }如果获取成功,则返回value值。如果map为空,则调用setInitialValue方法并返回value。
这里的getMap,是调用当期线程t,返回当前线程t中的一个成员变量threadLocals。
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
在Thread类中会发现有threadLocals与inheritableThreadLocals两个成员变量,都是ThreadLocal.ThreadLocalMap类。
ThreadLocalMap getMap(Thread t) { return t.threadLocals; }ThreadLocalMap这个类是ThreadLocal类的一个内部类:
public class ThreadLocal<T> { 。。。。。。 static class ThreadLocalMap { static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } } 。。。。。。 }可以看出ThreadLocalMap的Entry继承了WeakReference,并且使用ThreadLocal作为键值。
再看setInitialValue方法:
private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; } protected T initialValue() { return null; } void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }在setInitialValue方法中,会调用protected修饰的initialValue()方法, 默认情况下,initialValue方法返回的是null。所以在进行get之前,必须先set,否则会报空指针异常,如果想在get之前不需要调用set就能正常访问的话,必须重写initialValue()方法。
set方法就比较简单了:
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创建的副本是存储在每个线程自己的threadLocals中,threadLocals的类型ThreadLocalMap的键值为ThreadLocal对象,这样每个线程中可有多个threadLocal变量。
简单示例:
public class ThreadLocalTest { ThreadLocal<StringBuilder> a = new ThreadLocal<>(); ThreadLocal<String> b = new ThreadLocal<String>() { protected String initialValue() { return Thread.currentThread().getName() + "---zero"; } }; public static void main(String[] args) throws InterruptedException { final ThreadLocalTest test = new ThreadLocalTest(); System.out.println(test.a.get()); System.out.println(test.b.get()); Thread thread1 = new Thread() { public void run() { test.a.set(new StringBuilder("hello")); System.out.println(test.a.get()); System.out.println(test.b.get()); }; }; thread1.start(); thread1.join(); System.out.println(test.a.get()); System.out.println(test.b.get()); } }运行结果:
null main---zero hello Thread-0---zero null main---zero
InheritableThreadLocal类继承于ThreadLocal类,所以它具有ThreadLocal类的特性,但又是一种特殊的 ThreadLocal,其特殊性在于InheritableThreadLocal变量值会自动传递给所有子线程,即在创建子线程时,子线程会接收所有可继承的线程局部变量的初始值,以获得父线程所具有的值。而普通ThreadLocal变量不行。
如果一个子线程调用InheritableThreadLocal的get(),那么它将与它的父线程看到同一个对象。为保护线程安全性,应该只对不可变对象(一旦创建,其状态就永远不会被改变的对象)使用InheritableThreadLocal,因为对象被多个线程共享。
简单示例:
public class InheritableThreadLocalTest {
InheritableThreadLocal<StringBuilder> a = new InheritableThreadLocal<>();
InheritableThreadLocal<String> b = new InheritableThreadLocal<String>();
public static void main(String[] args) throws InterruptedException {
final InheritableThreadLocalTest test = new InheritableThreadLocalTest();
test.a.set(new StringBuilder("main---hello"));
test.b.set("main---zero");
System.out.println(test.a.get());
System.out.println(test.b.get());
Thread thread1 = new Thread() {
public void run() {
System.out.println(test.a.get());
System.out.println(test.b.get());
StringBuilder a = test.a.get().append("---subThread");
test.a.set(a);
test.b.set("subThread--zero");
System.out.println(test.a.get());
System.out.println(test.b.get());
};
};
thread1.start();
thread1.join();
System.out.println(test.a.get());
System.out.println(test.b.get());
}
}运行结果:
main---hello main---zero main---hello main---zero main---hello---subThread subThread--zero main---hello---subThread main---zero可以看出,如果InheritableThreadLocal存储的是可变性(mutable)的对象,如StringBuilder,对于主线程设置的值子线程可以通过get函数获取,但子线程调用set函数设置新值后,对主线程没有影响,对其它子线程也没有影响,只对自己可见,但如果子线程先get获取再修改对象的属性,那么这个修改对主线程和其它子线程是可见的,因为共享的是同一个引用。为了保护线程的安全性,一般建议只传递不可变(Immuable)对象,即没有状态的对象。
相关文章推荐
- tabtableView最下面加按钮
- 关于Android Studio报错java.lang.UnsatisfiedLinkError: Couldn't load faceppapi: findLibrary returned null
- Web Service 使用时出现 HTTP Status 401: Unauthorized
- jsp验证码
- java使用http创建https连接,并且使用http实现webservice服务端
- android 设置TextView水平滚动和解决首行缩进问题
- Asset Store
- 值得推荐的C/C++框架和库
- Jira plugin cannot startup due to "Error parsing class file"
- MySQL 语句级避免重复插入—— Insert Select Not Exist
- 时间分析法:amr 12.2k mips
- 黑马程序员————Java基础之IO流
- 抽象工厂模式【Abstract Factory Pattern 】
- 127.0.0.1与host文件
- 传统企业如何享受大数据带来的红利
- Java再说JNI之实例
- HDU2444(二分图)
- DockTitle以及消息通知提示的研究
- 第二章 管理数据库和表
- ES5严格模式(Strict mode)