本文内容基于jdk1.8版本
变量值的共享可以使用public static变量的形式,所有的线程都使用同一个public static变量。
如果想实现每一个线程都有自己的共享变量,则可以用JDK中提供的类ThreadLocal.
ThreadLocal类就像一个全局存放数据的盒子,盒子中可以存储每个线程的私有数据。
ThreadLocal解决的是同一个线程内的资源共享问题,而synchronized 解决的是多个线程间的资源共享问题
对于多线程资源共享的问题,synchronized同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
测试隔离性例子:
新建项目test1,新建类LocalData:
package test1;
public class LocalData {
public static ThreadLocal t1 = new ThreadLocal();
}
新建类ThreadA和ThreadB:
package test1;
public class ThreadA extends Thread{
public void run(){
try{
for(int i = 0; i < 100; i++){
LocalData.t1.set("ThreadA" + (i + 1));
System.out.println("ThreadA get Value="+ LocalData.t1.get());
Thread.sleep(200);
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
package test1;
public class ThreadB extends Thread {
public void run() {
try {
for (int i = 0; i < 100; i++) {
LocalData.t1.set("ThreadB" + (i + 1));
System.out.println("ThreadB get Value=" + LocalData.t1.get());
Thread.sleep(200);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
新建main入口类Run
package test1;
public class Run {
public static void main(String[] args) {
try {
ThreadA a = new ThreadA();
ThreadB b = new ThreadB();
a.start();
b.start();
for (int i = 0; i < 100; i++) {
LocalData.t1.set("Main" + (i + 1));
System.out.println("Main get Value=" + LocalData.t1.get());
Thread.sleep(200);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
image.png
要设置get的默认值,覆写initialValue方法:
import java.time.Instant;
public class ThreadLocalExt extends ThreadLocal {
@Override
protected Object initialValue(){
return Instant.now().getNano();
}
}
类InheritableThreadLocal
类InheritableThreadLocal可以在子线程中取得父线程继承下来的值。
值继承例子:
新建项目test2,新建InheritableThreadLocalExt类
package test2;
import java.time.Instant;
public class InheritableThreadLocalExt extends InheritableThreadLocal {
@Override
protected Object initialValue(){
return Instant.now().getNano();
}
@Override
protected Object childValue(Object parentValue){
return parentValue + " 子线程新加内容,i'm child value";
}
}
新建ThreadA类:
package test2;
public class ThreadA extends Thread {
public void run() {
try {
for (int i = 0; i < 10; i++) {
System.out.println("Thread线程中取值=" + LocalData2.t1.get());
Thread.sleep(100);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
新建LocalData2类:
package test2;
public class LocalData2 {
public static InheritableThreadLocalExt t1 = new InheritableThreadLocalExt();
}
新main入口类:
package test2;
public class Run {
public static void main(String[] args) {
try {
for (int i = 0; i < 10; i++) {
System.out.println("Main 线程中取值=" + LocalData2.t1.get());
Thread.sleep(100);
}
Thread.sleep(5000);
ThreadA a = new ThreadA();
a.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
image.png
源码解析:
ThreadLocal类set和get方法:
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 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();
}
每个线程中都有一个独立的ThreadLocalMap副本,它所存储的值,只能被当前线程读取和修改。ThreadLocal类通过操作每一个线程特有的ThreadLocalMap副本,从而实现了变量访问在不同线程中的隔离。因为每个线程的变量都是自己特有的,完全不会有并发错误。