ThreadLocal ,即线程变量,是一个以ThreadLocal对象为键,任意对象为值得存储接口。这个接口被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的值。
可以通过set(T)方法来设置一个值,在当前线程下,在通过get()方法获取到原先设置的值。
上面的文字是不是有点晦涩?来,学习一下明白的。
变量值得共享可以使用 public static 变量的形式,所有的线程都使用同一个 public static 变量。如果想实现每一个线程都有自己的共享变量该如何解决那?
jdk提供了ThreadLocal正是为了解决这样的问题。
类ThreadLocal 主要解决的就是每个线程绑定自己的值,可以将ThreadLocal类比喻成全局存放数据的盒子,盒子中可以存储每个线程的私有数据。
下面实验:
1 创建ThreadLocal对象,用来存储每个线程的私有值。
public class Tools { public static ThreadLocal t=new ThreadLocal(); }
2 创建两个线程A,B.
public class ThreadA extends Thread { @Override public void run() { super.run(); try { for(int i=0;i<100;i++){ Tools.t.set("ThreadA "+(i+1)); System.out.println("ThreadA get Value " + Tools.t.get()); Thread.sleep(200); } } catch (Exception e) { e.printStackTrace(); } } }
public class ThreadB extends Thread{ @Override public void run() { super.run(); try { for(int i=0;i<100;i++){ Tools.t.set("ThreadB "+(i+1)); System.out.println("ThreadB get Value "+Tools.t.get()); Thread.sleep(200); } } catch (Exception e) { e.printStackTrace(); } } }
主线程:
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++) { Tools.t.set("main "+(i+1)); System.out.println("main get Value "+Tools.t.get()); Thread.sleep(200); } } catch (Exception e) { e.printStackTrace(); } } }
控制台:
ThreadB get Value ThreadB 1 ThreadA get Value ThreadA 1 main get Value main 1 ThreadA get Value ThreadA 2 main get Value main 2 ThreadB get Value ThreadB 2 main get Value main 3 ThreadA get Value ThreadA 3 ThreadB get Value ThreadB 3 ThreadA get Value ThreadA 4 ThreadB get Value ThreadB 4 main get Value main 4 ThreadB get Value ThreadB 5 ThreadA get Value ThreadA 5 main get Value main 5 main get Value main 6 ThreadB get Value ThreadB 6 ThreadA get Value ThreadA 6
可以发现,ThreadA,ThreadB,和主线程三个在ThreadLocal中存储的值互不影响,每个线程增加,取值,都是自己的私有的。ThreadLocal中存储的值具有隔离性。
使用类InheritableThreadLocal类可以让子线程中取得父线程中的值,并修改。
下面使用ThreadLocal来模拟统计五个线程走完一段代码消耗的时间的问题。
首先创建一个常用的Profiler类
public class Profiler { //第一次get()方法调用的时候会进行初始化(前提是set方法未调用),每个线程都会调用一次。 private static final ThreadLocal<Long> TIME_THREADLOCAL=new ThreadLocal<Long>(){ protected Long initialValue() { return System.currentTimeMillis(); }; }; public static final void begin(){ TIME_THREADLOCAL.set(System.currentTimeMillis()); } public static final Long end(){ return System.currentTimeMillis()-TIME_THREADLOCAL.get(); } }
主线程中开启五个线程,并调用begin()和end()方法。(关于未调用set直接调用get返回是null的情况,注释已经解释解决办法。也可以通过继承ThreadLocal类,然后重写initialValue()方法改变初始化的值);
public class Run { public static void main(String[] args) { for (int i = 0; i < 5; i++) { final int temp=i; Thread thread=new Thread(new Runnable() { @Override public void run() { try { Profiler.begin(); Thread.sleep(temp*1000); System.out.println("线程"+Thread.currentThread().getName()+"消耗时间 "+Profiler.end()); } catch (Exception e) { e.printStackTrace(); } } }); thread.start(); } } }
控制台:
线程Thread-0消耗时间 0 线程Thread-1消耗时间 1000 线程Thread-2消耗时间 2000 线程Thread-3消耗时间 3001 线程Thread-4消耗时间 4001
可以发现,五个线程互不影响,各自统计自己的消耗的时间。
每一个优秀的人,都有一段沉默的时光。不抱怨,不诉苦,最后度过那段感动自己的日子。