多线程学习总结----ThreadLocal及InheritableThreadLocal介绍
ThreadLocal及InheritableThreadLocal
1.什么是ThreadLocal?
简单来说,这时JDK提供的.当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本.
代码演示:
package com.my.thread;
public class ThreadDemo2 {
private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
@Override
public Integer initialValue() {
return 0;
}
};
static class ThreadDemo implements Runnable {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
threadLocal.set(threadLocal.get() + 1);
}
System.out.println("thread :" + Thread.currentThread().getId() + " is" + threadLocal.get());
}
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(new ThreadDemo()).start();
}
}
}
结果:
thread :11 is1000
thread :12 is1000
thread :13 is1000
thread :14 is1000
thread :17 is1000
thread :16 is1000
thread :15 is1000
thread :18 is1000
thread :19 is1000
thread :20 is1000
从上面的结果可以看出,输出都是1000.说明线程之间运算是没有影响的.并没因为共享属性而造成运算混乱问题.
ThreadLocal原理剖析 (部分代码)
ThreadLocal.ThreadLocalMap threadLocals = null;
protected T initialValue() {
return null;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
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();
}
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;
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
总结
从最开始我们使用ThreadLocal的时候,可以重写initialValue()方法,给其赋初始值.
1.如果先要调用set(T value)方法设置值,这时会先使用getMap(t)获取当前线程ThreadLocalMap集合对象.
判断如果为null,会通过createMap(Thread t, T firstValue)来创建和赋值.
如果不为null,说明已经有了ThreadLocalMap集合,直接往集合里添加即可.
2.如果先调用get()方法,也会先去getMap(t)获取当前线程的ThreadLocalMap集合对象.
如果不为null,这从ThreadLocalMap集合中获取.
如果为null,则会执行setInitialValue()方法,并通过createMap(Thread t, T firstValue)来创建和赋值.
到这里,也应该明白了吧.我们在定义的ThreadLocal对象时,并没有为其线程创建ThreadLocalMap集合.而是在调用get()和set方法时,去判断创建的.
所以也就是每个线程都有自己的一个ThreadLocalMap集合,用于自己线程使用.
那么当线程结束又是怎么处理的呢?
private void exit() {
if (group != null) {
group.threadTerminated(this);
group = null;
}
target = null;
threadLocals = null;
inheritableThreadLocals = null;
inheritedAccessControlContext = null;
blocker = null;
uncaughtExceptionHandler = null;
}
从上面可以看到,如果线程结束会调用exit()方法.会执行一句threadLocals = null;这时ThreadLocalMap集合就会空引用了,等待GC前来清理销毁.
这样处理完全没问题,但是用在线程池里面就不一样.
package com.my.thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadLocalTest {
//创建一个Integer本地线程变量
private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
@Override
public Integer initialValue() {
return 0;
}
};
static class ThreadDemo implements Runnable {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
threadLocal.set(threadLocal.get() + 1);
}
System.out.println("thread :" + Thread.currentThread().getId() + " is" + threadLocal.get());
//threadLocal.remove();
}
}
public static void main(String[] args) {
ExecutorService executorService= Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
executorService.submit(new Thread(new ThreadDemo()));
}
}
}
结果:
thread :12 is1000
thread :16 is1000
thread :18 is1000
thread :16 is2000
thread :16 is3000
thread :16 is4000
thread :18 is2000
thread :12 is2000
thread :14 is1000
thread :20 is1000
可以看出来,同一个线程时累加的.这也是因为在线程池的线程在执行完后,没有被杀死,而是又回到线程次中等待下一个任务.
当我们把//threadLocal.remove();的注释打开后,结果就如下面所示了。
结果:
thread :14 is1000
thread :14 is1000
thread :14 is1000
thread :14 is1000
thread :12 is1000
thread :14 is1000
thread :12 is1000
thread :16 is1000
thread :18 is1000
thread :20 is1000
可以看到,结果都是1000.所以在线程池中使用ThreadLocal时,只要在使用完后清空ThreadLocal的值,就可以正常使用了.
2.InheritableThreadLocal
为什么要用这个InheritableThreadLocal?
首先,先说一下需求场景.当我们在一个线程A中启动线程B去执行一个操作,但是我们需要在线程B中使用到线程A的ThreadLocal中的一些值.这时就需要线程A的ThreadLocal可以传递到线程B中.
代码演示场景:
package com.my.thread;
public class ThreadLocalTest1 {
private static InheritableThreadLocal<Integer> inheritableThreadLocal = new InheritableThreadLocal<Integer>();
static class ThreadDemo implements Runnable {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
inheritableThreadLocal.set(inheritableThreadLocal.get() + 1);
}
System.out.println("thread :" + Thread.currentThread().getId() + " is" + inheritableThreadLocal.get());
}
}
public static void main(String[] args) {
inheritableThreadLocal.set(24);
for (int i = 0; i < 10; i++) {
new Thread(new ThreadDemo()).start();
}
}
}
结果:
thread :12 is1024
thread :11 is1024
thread :13 is1024
thread :14 is1024
thread :17 is1024
thread :18 is1024
thread :19 is1024
thread :20 is1024
thread :15 is1024
thread :16 is1024
从上面可以看到,来自main线程的24被传递到了子线程中.最后打印1024
InheritableThreadLocal原理剖析
InheritableThreadLocal源码代码:
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
protected T childValue(T parentValue) {
return parentValue;
}
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
Thread类的部分代码:
public class Thread implements Runnable {
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name.toCharArray();
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
if (security != null) {
g = security.getThreadGroup();
}
/* If the security doesn't have a strong opinion of the matter
use the parent thread group. */
if (g == null) {
g = parent.getThreadGroup();
}
}
/* checkAccess regardless of whether or not threadgroup is
explicitly passed in. */
g.checkAccess();
/*
* Do we have the required permissions?
*/
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
if (parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
tid = nextThreadID();
}
}
简单总结:
这里可以看到,InheritableThreadLocal实际上是继承了ThreadLocal,并重写了getMap(Thread t)和createMap(Thread t, T firstValue)和childValue(T parentValue)三个方法。值赋值map的集合是给了线程的inheritableThreadLocals变量。
而我们在看Thread源码的时候,我们就可以发现在init()方法中有一个:
Thread parent = currentThread();
this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
看到这里,相信你也明白了怎么回事了。
简单来说,在我们使用InheritableThreadLocal的时候.和使用ThreadLocal一样.只是子线程创建完线程并初始化中,会把当前父线程的inheritableThreadLocals传递到子线程中.这样就会在子线程中使用到父线程的数据了。
比如上述事例,就是在main线程中设置了线程main的inheritableThreadLocal中的值为24,在启动子线程的时候,就会把带有24的inheritableThreadLocal传递到子线程中。然后子线程再用对其inheritableThreadLocal(已有初始值24)做累加操作,最后结束得到1024