Java多线程编程-ThreadLocal和InheritableThreadLocal使用及其源码分析
如果我们想为每一个线程都有自己的共享变量,那我们可以使用ThreadLocal,如果我们使用public static是所有线程都可以使用。
ThreadLocal使用
提供线程局部变量。这些变量与普通变量不同,因为每次访问一个线程(通过其get或set方法)的线程都有其自己的,独立初始化的变量副本。ThreadLocal实例通常是希望将状态与线程关联的类中的私有静态字段
只要线程是活动的并且ThreadLocal实例是可访问的,则每个线程都对其线程局部变量的副本持有隐式引用。线程消失后,其线程本地实例的所有副本都将进行垃圾回收(除非存在对这些副本的其他引用)
ThreadLocal相关API
get()方法和set()方法
get()方法:返回该线程局部变量在当前线程副本中的值
public T get() {
//获取当前线程
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
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);
}
简单试试这两个方法:
public class ThreadLocalV1Main {
public static ThreadLocal<String> threadLocal = new ThreadLocal<>();
public static void main(String[] args) {
if (threadLocal.get() == null) {
System.out.println("current thread the threadlocal is null " + Thread.currentThread().getName());
threadLocal.set("set value :" + Thread.currentThread().getName());
}
System.out.println("value is " + threadLocal.get());
}
}
运行结果如下:
我们可以通过set和get方法源码可以看到,获取当前对象中值,最终会存储到一个Map(ThreadLocalMap:ThreadLocal的静态内部类)中,这个是以线程为key,保存相应的值。因此可以通过线程获取值。因此每个线程中的值是每个线程特有的。
我们现在验证一下ThreadLocal在多线程下每个线程之间独立保存一份
对象类:
public class ThreadLocalObj {
public static ThreadLocal<String> threadLocal = new ThreadLocal<>();
}
线程1:
public class ThreadLocalT1 extends Thread {
public ThreadLocalT1(String name) {
this.setName(name);
}
@Override
public void run() {
try {
for (int i = 0; i < 100; i++) {
System.out.println("current thread set thread local value,current thread name:" + Thread.currentThread().getName() + "and i = " + i);
ThreadLocalObj.threadLocal.set("current thread name:" + Thread.currentThread().getName());
System.out.println("current get thread local value:" + ThreadLocalObj.threadLocal.get());
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
线程2:
public class ThreadLocalT2 extends Thread {
public ThreadLocalT2(String name) {
this.setName(name);
}
@Override
public void run() {
try {
for (int i = 0; i < 100; i++) {
System.out.println("current thread set thread local value,current thread name:" + Thread.currentThread().getName() + "and i = " + i);
ThreadLocalObj.threadLocal.set("current thread name:" + Thread.currentThread().getName());
System.out.println("current get thread local value:" + ThreadLocalObj.threadLocal.get());
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行:
public class ThreadLocalV2Main {
public static void main(String[] args) throws InterruptedException {
ThreadLocalT1 threadLocalT1 = new ThreadLocalT1("thread01");
threadLocalT1.start();
ThreadLocalT2 threadLocalT2 = new ThreadLocalT2("thread02");
threadLocalT2.start();
for (int i = 0; i < 100; i++) {
System.out.println("current thread set thread local value,current thread name:" + Thread.currentThread().getName() + "and i = " + i);
ThreadLocalObj.threadLocal.set("current thread name:" + Thread.currentThread().getName());
System.out.println("current get thread local value:" + ThreadLocalObj.threadLocal.get());
Thread.sleep(1000);
}
}
}
运行结果:
有运行结果可以得出,三个线程(thread01,thread02和Main)都是取各自线程中的值。
initialValue()
我们上面使用get方法有可能为null,但是想ThreadLocal初始值不为null怎么处理?就可以使用这个方法了。
initialValue()方法:返回当前线程的这个线程局部变量的“初始值”
protected T initialValue() {
return null;
}
这个方法是一个protected ,我们通过子类从写一下这个方法。
public class SubThreadLocal extends ThreadLocal<String> {
@Override
protected String initialValue() {
return "default value";
}
}
运行类:
public class SubThreadLocalMain {
public static SubThreadLocal threadLocal = new SubThreadLocal();
public static void main(String[] args) {
System.out.println(threadLocal.get());
}
}
运行结果:
表明ThreadLocal设置成功,ThreadLocal提供了一个私有的setInitialValue供get方法使用
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;
}
这里只也是通过initialValue方法设置的。
remove()
上面有设置和获取方法,作为保存在map中的对象,肯定是有remove方法,不然不让我删除多尴尬!
remove():删除此线程局部变量的当前线程值
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
我们使用看看:
public class SubThreadLocalMain {
public static SubThreadLocal threadLocal = new SubThreadLocal();
public static void main(String[] args) {
System.out.println(threadLocal.get());
threadLocal.set("this is a set value");
System.out.println(threadLocal.get());
threadLocal.remove();
System.out.println(threadLocal.get());
}
}
运行结果:
输出结果的先输出初始值,后面输出设置的值,在进行remove操作,在调用get获取的为默认值。
InheritableThreadLocal类
此类扩展了ThreadLocal来提供从父线程到子线程的值的继承:创建子线程时,子级将接收父级具有值的所有可继承线程局部变量的初始值。 通常,子级的价值观将与父级的价值观相同。 但是,通过覆盖此类中的childValue方法,可以使子级的值成为父级的任意函数。 当必须将在变量中维护的每个线程属性自动传输到创建的任何子线程时,可继承的线程局部变量将优先于普通线程局部变量使用。
源码:
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
/**
*在创建子线程时,根据父级值计算此可继承线程局部变量的子级初始值。在启动子级之前,从父线程中调用此方法。 此方法仅返回其输入参数,如果需要其他行为,则应重写此方法。
*/
protected T childValue(T parentValue) {
return parentValue;
}
/**
* 获取与ThreadLocal关联的ThreadLocalMap
*/
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
/**
创建与ThreadLocal关联的Map
*/
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
我们来操作一下:
public class SubInheritableThreadLocal extends InheritableThreadLocal<String> {
@Override
protected String initialValue() {
return "default value";
}
}
public class InheritableThreadLocalObj {
public static SubInheritableThreadLocal subInheritableThreadLocal = new SubInheritableThreadLocal();
}
public class InheritableThreadLocalT1 extends Thread {
@Override
public void run() {
try {
for (int i = 0; i < 100; i++) {
System.out.println("current thread name:" + currentThread().getName() + " value:" + InheritableThreadLocalObj.subInheritableThreadLocal.get());
Thread.sleep(1000);
}
} catch (InterruptedException interruptedException) {
interruptedException.printStackTrace();
}
}
}
public class InheritableThreadLocalV1 {
public static void main(String[] args) {
try {
for (int i = 0; i < 10; i++) {
System.out.println("current thread name :" + Thread.currentThread().getName() + " value:" + InheritableThreadLocalObj.subInheritableThreadLocal.get());
}
Thread.sleep(1000);
InheritableThreadLocalT1 inheritableThreadLocalT1 = new InheritableThreadLocalT1();
inheritableThreadLocalT1.setName("thread01");
inheritableThreadLocalT1.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
那我们如何修改子线程中的值呢?我们只需要重写childValue方法就好了。
修改的代码:
public class SubInheritableThreadLocal extends InheritableThreadLocal<String> {
@Override
protected String initialValue() {
return "default value";
}
@Override
protected String childValue(String parentValue) {
return "this is a sub thread name value " + Thread.currentThread().getName();
}
}
运行结果:
这个就是打印了子线程修改过的值。这里要注意一点,如果子线程在进行get调用的时候,父线程在set的时候,子线程获取的还是之前的旧值。