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
可以发现,五个线程互不影响,各自统计自己的消耗的时间。
每一个优秀的人,都有一段沉默的时光。不抱怨,不诉苦,最后度过那段感动自己的日子。
浙公网安备 33010602011771号