先看一个简单的实例:
假如有两个线程 A、B 要同时先后访问 TestValue 的set(key)、getKey()方法,那么线程 A 和线程 B get出来的值可能不安全,如下示例:
public class TestValue {
public static String key;
public static String getKey() {
return key;
}
public static void setKey(String key) {
TestValue.key = key;
}
public static void main(String[] args) {
new Thread() {
@Override
public void run() {
setKey("A");
try {
Thread.sleep(1000);
System.out.println("A thread-->"+getKey());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
new Thread() {
@Override
public void run() {
setKey("B");
try {
Thread.sleep(1000);
System.out.println("B thread-->"+getKey());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
}
实际执行结果为:
A thread-->B
B thread-->B
但我们想要的结果为:
A thread-->A
B thread-->B
所以ThreadLocal可以处理多线程下每个线程变量值的存放安全问题,ThreadLocal可以把每个线程自己想要 set 的变量放到本地线程变量中,而不是放到共享变量中,这样可以解决多线程操作变量的安全问题。
把上面的代码可以进行改造,如下:
public class TestValue {
public static ThreadLocal<String> key = new ThreadLocal<>();
public static String getKey() {
return key.get();
}
public static void setKey(String str) {
key.set(str);
}
public static void main(String[] args) {
new Thread() {
@Override
public void run() {
setKey("A");
try {
Thread.sleep(1000);
System.out.println("A thread-->"+getKey());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
new Thread() {
@Override
public void run() {
setKey("B");
try {
Thread.sleep(1000);
System.out.println("B thread-->"+getKey());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
}
结果一直为:
A thread-->A
B thread-->B
以上代码创建一个ThreadLocal对象做为成员变量,即相当于创建了每一个线程的本地变量,我们在set的时候,其实是把变量放到了每个线程的本地变量(每个线程的成员变量),通过get拿的时候也是从线程本地存放的变量中取,这样线程和线程之间相互独立,互不影响。
因为第一个版本代码,我们每次set的时候是放到了共享变量中key中,两个线程同时对这个变量都有操作,所以存在安全问题。第二个版本代码,我们利用ThreadLocal创建了线程本地变量,set 和 get 的时候由第一个版本的共享变量变为第二个版本的线程私有的变量,这个变量存在每个线程中,互不影响,所以没有安全问题。
总结,ThreadLocal解决的不是多线程访问共享变量的安全问题,而是解决多线程每个线程操作变量值的存放问题,至于多线程造成的安全问题,是因为你把线程的变量放到了共享变量中,ThreadLocal是把变量放到了线程私有变量中。
至于ThreadLocal中关键的get()、set(T t)、remove()、initialValue() 有兴趣可以自己看,网上也很多。