变量值的共享可以通过使用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中的值进行了修改,则子线程中的值不同步更新,依然取到的是旧的值。