变量值的共享可以通过使用public static的形式,使得一个类的所有实例对象共享类中的同一个变量。但是如果想要实现每个线程都有自己的一个共享变量该如何解决呢?这个时候就要用到ThreadLocal类了。ThreadLocal类就是用来解决每个线程都能绑定自己的值,可以把ThreadLocal类看成一个全局的存放数据的盒子,盒子中可以存放每个线程自己的数据。
一、使用ThreadLocal类
public class Run {
public static ThreadLocal tl = new ThreadLocal();
public static void main(String args[]) {
if(tl.get()==null) {
System.out.println("ThreadLocal还没有被赋值。");
tl.set(9527);
}
System.out.println(tl.get());
}
}
运行程序可以看到结果为:
/*
ThreadLocal还没有被赋值。
9527
*/
可以看到,ThreadLocal类中的get方法第一次被调用的时候返回null,而在set之后再调用get方法返回的就是存入的新值了。ThreadLocal主要是为了保证变量在不同线程之间的隔离性,也就是说对于一个变量而言在不同线程内部拥有不同的值,这时就可以用ThreadLocal类来保存这些值。
下面的例子验证了ThreadLocal变量的值在不同线程间的隔离性:
//ThreadLocal类用来创建共享变量
public class Tools {
public static ThreadLocal tl = new ThreadLocal();
}
public class ThA extends Thread{
@Override
public void run() {
try {
for(int i=0;i<10;i++) {
Tools.tl.set("Thread-A="+(i+1));
System.out.println(Tools.tl.get());
Thread.sleep(500);
}
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
public class ThB extends Thread{
@Override
public void run() {
try {
for(int i=0;i<10;i++) {
Tools.tl.set("Thread-B="+(i+1));
System.out.println(Tools.tl.get());
Thread.sleep(500);
}
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
public class Run {
public static void main(String args[]) {
try {
ThA tha = new ThA();
ThB thb = new ThB();
tha.start();
thb.start();
for(int i=1;i<10;i++) {
Tools.tl.set("Thread-Main="+(i+1));
System.out.println(Tools.tl.get());
Thread.sleep(500);
}
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
部分输出结果为:
/*
Thread-Main=2
Thread-B=1
Thread-A=1
Thread-A=2
Thread-B=2
Thread-Main=3
Thread-A=3
Thread-Main=4
Thread-B=3
Thread-B=4
Thread-A=4
Thread-Main=5
Thread-A=5
Thread-Main=6
*/
可以看到,两个子线程和主线程交替运行,三个线程各自向tl中存入属于自己线程的值,每个线程在调用tl对象的get方法时可以获得相应的自己的值。
ps:上面两个例子ThreadLocal实例化的时候是没有初始值的,也就是说,第一次调用get会返回null。如果想在ThreadLocal创建实例对象的同时初始化它的值,则可以创建一个类继承ThreadLocal,其中重写ThreadLocal类中的initialValue()方法,使其返回我们所需要的初始值即可。
二、InheritableThreadLocal类的值继承
使用类InheritableThreadLocal类可以实现在子线程中继承来自主线程中的ThreadLocal的值。
//继承InheritableThreadLocal,并重写initialValue方法
public class InheritableThreadLocalExt extends InheritableThreadLocal{
@Override
protected Object initialValue() {
return new Date().getTime();
}
}
public class Tool {
public static InheritableThreadLocalExt tl = new InheritableThreadLocalExt();
}
public class ThA extends Thread{
@Override
public void run() {
try {
for(int i=0;i<3;i++) {
System.out.println("子线程的值:"+Tool.tl.get());
Thread.sleep(500);
}
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
public class Run {
public static void main(String args[]) {
try {
for(int i=0;i<3;i++) {
System.out.println("main线程中的值:"+Tool.tl.get());
Thread.sleep(500);
}
Thread.sleep(2000);
System.out.println("currentTimeMillis="+System.currentTimeMillis());
ThA tha = new ThA();
tha.start();
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
在主线程中,先给获取tl的值并进行打印,然后主线程sleep一段时间,接着创建子线程tha,并启动tha线程输出tha存储在tl中的值。可以看到以下输出结果:
/*
main线程中的值:1526630816087
main线程中的值:1526630816087
main线程中的值:1526630816087
currentTimeMillis=1526630819597
子线程的值:1526630816087
子线程的值:1526630816087
子线程的值:1526630816087
*/
子线程继承了主线程中的InheritableThreadLocal中的值。
如果不想让子线程继承主线程中的共性变量的值,只需要在InheritableThreadLocalExt中重写以下方法:
@Override
protected Object childValue(Object parentValue) {
return parentValue+"子线程重新初始化";
}
则从子线程中获取tl共享变量的值时,将不再和主线程中的值一样。
ps:如果子线程在取得值的同时,主线程中对InheritableThreadLocal中的值进行了修改,则子线程中的值不同步更新,依然取到的是旧的值。