JDK 1.2的版本开始提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
在多线程环境下,每个线程都有自己的数据。一个线程使用自己的局部变量比使用全局变量好,因为局部变量只有线程自己能看见,不会影响其他线程。
从线程的角度看,目标变量就像是线程的本地变量,这也是类名中“Local”所要表达的意思。
上面陈述比较抽象,下面我们通过例子去说明。
public class ThreadLocalTest {
//jdk建议ThreadLocal定义为private static
private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
}
这样,ThreadLocalTest该类中就有了一块名为threadLocal的大大的土地,里面有一个Integer类型的变量,默认值为null。
然后每个线程进来这块大土地里取值的时候,这块大土地会给该线程分一小亩地,还会把那个Integer类型的变量形成一个副本丢进这一小亩土地,其值还是为null。因此,每个线程进来取值的时候,取的都是属于自己那一小亩地的Integer类型的变量,互不干扰。
假设有thread-1和thread-2两个线程要从threadLocal取值,那按上述所讲,就是下图所示。
threadLocal这块大土地,两个线程都要从大土地中拿值,此时大土地分别给thread-1和thread-2两个线程各自分配两块互不相关的土地,并且里面的值是大土地的副本,因此thread-1取值是从属于thread-1的土地中取值,thread-2取值是从属于thread-2的土地中取值,所以它们两个线程互不干扰对方,每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
ThreadLocal主要有三个方法,分别是get / set / initialValue.
首先我们看一下上述的这行代码。
private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
因为threadLocal中的Integer类型没有赋初值,因此为null,现在我们想更改一下threadLocal的初始值,因此我们重写ThreadLocal类中的initialValue这个方法。把上面的那行代码改为如下这行代码。那么threadLocal中的Integer类型的变量的初始值就变成200了。
private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return 200;
}
}
然后,如果某个线程在执行过程中调用了treadLocal.get()方法,那么该线程就从自己那一亩地中取到属于它自己的值。
同理,如果某个线程在执行过程中调用了threadLocal.set(XXX)方法,那么该线程就把自己那一亩地的那个值修改成XXX。
我们看个完整的代码,来总结今天的所有内容。
public class ThreadLocalTest {
private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return 200;
}
};
//main主线程
public static void main(String[] args) {
//threadLocal.get()获取的是main主线程的值,是200
System.out.println(Thread.currentThread().getName() + "-->" + threadLocal.get());
//threadLocal.set(99)修改的是main主线程中的值,修改为99了
threadLocal.set(99);
System.out.println(Thread.currentThread().getName() + "--->" + threadLocal.get());
new Thread(new MyRun()).start(); //thread-01
new Thread(new MyRun()).start(); //thread-02
}
public static class MyRun implements Runnable{
@Override
public void run() {
//若此时是thread-01线程在执行,则修改的是thread-01线程里的值
//若此时是thread-02线程在执行,则修改的是thread-02线程里的值
threadLocal.set((int)(Math.random()*100));
//若此时是thread-01线程在执行,则得到的是thread-01线程里的值
//若此时是thread-02线程在执行,则得到的是thread-02线程里的值
System.out.println(Thread.currentThread().getName() + "--->" + threadLocal.get());
}
}
}
本文主要讲述了ThreadLocal的主要原理和使用方法,如果你们感兴趣的话可以去看看ThreadLocal的实现源码,相信你们会更加地明白编程的艺术!
【完】