多线程访问同一个变量的时候,很容易出现问题,特别是多线程对一个共享变量进行写入的时候。为了线程的安全在进行数据写入时候会进行数据的同步。
如上图: 如果要想实现多个线程操作同一个数据而不被其他线程所影响,就必须加锁操作,加锁又增加了使用的复杂度。
ThredLocal的用处就是当多个线程操作同一个变量的时候可以在创建一个ThredLocal,把共享变量复制到线程的本地threadLocals(threadLocals是Thread的成员变量) 中,这样各个线程进行操作的时候只能操作自己本地数据,这样就不会存储在线程不安全的问题了。
下面举个例子:
public class ThreadLocalTest {
static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
static Integer num = 5;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
threadLocal.set(num);
System.out.println("线程1: " + threadLocal.get());
}).start();
new Thread(()->{
System.out.println("线程2: "+ threadLocal.get());
}).start();
System.out.println("主线程: " + threadLocal.get());
}
}
结果: 由上面的返回可以知道,当在线程1设置了设置了数据时在线程2是访问不到的**,主线程**也访问不到。
ThreadLocal的实现原理:
由下图可知 Thread类中有一个成员变量 ThreadLocal.ThreadLocalMap **threadLocals **= **null ** 是ThreadLocalMap类型的,本地变量都是存储在ThreadLocalMap中的,ThreadLocal只是一个外壳。ThreadLocalMap是ThreadLocal中的内部类,是一个定制化的Map形式,那就代表一个线程可以有多个ThreadLocal本地变量。key就是ThreadLocal本身,value就是存储的本地变量。
1.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);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
2.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();
}
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;
}
3. remove()
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
ThreadLocal不具备继承性
从上面的set接口 和 get接口就不难看出我们现在存储、获取、删除数据都是在当前线程threadLocals成员变量中存储的。所以父线程和存储的数据在子线程中获取不了,在子线程中存储的数据在父线程中存储不了。
例:
public class ThreadLocalTest {
static ThreadLocal<Integer> stringThreadLocal = new ThreadLocal<>();
static Integer num = 5;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
stringThreadLocal.set(num);
System.out.println("线程1: " + stringThreadLocal.get());
new Thread(()->{
System.out.println("线程1的子线程: " + stringThreadLocal.get());
}).start();
}).start();
}
}
结果:
InheritableThreadLocal类
为了解决上述问题,为了让子类能够访问父类的数据,则InheritableThreadLocal应用而生。
- 首先InheritableThreadLocal类继承了ThreadLocal类 并重写了ThreadLocla的三个方法
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创建的init方法
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
......
Thread parent = currentThread();
........
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
}
- 这样父类线程的inheritThreadLocals数据就存储到了子类变量中去了。
- 值得注意的事,虽然能用父类的放进去的值,但是不影响父类的操作,也就是子类和父类的值是隔离开的。
package com.example.spring;
public class ThreadLocalTest {
static ThreadLocal<Integer> stringThreadLocal = new ThreadLocal<>();
static InheritableThreadLocal<Integer> integerInheritableThreadLocal = new InheritableThreadLocal();
static Integer num = 5;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
integerInheritableThreadLocal.set(num);
System.out.println("线程1: " + integerInheritableThreadLocal.get());
new Thread(()->{
integerInheritableThreadLocal.set(4);
System.out.println("线程1的子线程: " + integerInheritableThreadLocal.get());
}).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1的最终结果:" + integerInheritableThreadLocal.get());
}).start();
}
}
|