ThreadLocal:线程局部变量
在并发编程的时候,成员变量如果不做任何处理其实是线程不安全的,各个线程都在操作同一个变量,显然是不行的,并且我们也知道volatile这个关键字也是不能保证线程安全的。那么在有一种情况之下,我们需要满足这样一个条件:变量是同一个,但是每个线程都使用同一个初始值,也就是使用同一个变量的一个新的副本。这种情况之下ThreadLocal就非常使用
例子比较:
不使用ThreadLocal时
public class ThreadLocalTest {
static int a =1;
public static void main(String[] args) {
new Thread1().start();
new Thread2().start();
new Thread3().start();
}
}
class Thread1 extends Thread{
@Override
public void run() {
for (int i = 30; i < 40; i++) {
ThreadLocalTest.a = i;
}
System.out.println("最后1====>"+ThreadLocalTest.a);
}
}
class Thread2 extends Thread{
@Override
public void run() {
for (int i = 60; i < 70; i++) {
ThreadLocalTest.a = i;
}
System.out.println("最后2====>"+ThreadLocalTest.a);
}
}
class Thread3 extends Thread{
@Override
public void run() {
for (int i = 80; i < 90; i++) {
ThreadLocalTest.a = i;
}
System.out.println("最后3====>"+ThreadLocalTest.a);
}
}
结果可以看到
当多个线程访问这个成员变量时,它会被其他线程改变
最后2====>69
最后1====>69
最后3====>89
使用ThreadLocal时
public class ThreadLocalTest {
static int a =1;
public static void main(String[] args) {
new Thread1().start();
new Thread2().start();
new Thread3().start();
}
}
class Thread1 extends Thread{
@Override
public void run() {
for (int i = 10; i < 20; i++) {
ThreadLocalTest.integerThreadLocal.set(i);
System.out.println("thread1====>"+ThreadLocalTest.integerThreadLocal.get());
}
}
}
class Thread2 extends Thread{
@Override
public void run() {
for (int i = 40; i < 50; i++) {
ThreadLocalTest.integerThreadLocal.set(i);
System.out.println("thread2====>"+ThreadLocalTest.integerThreadLocal.get());
}
}
}
class Thread3 extends Thread{
@Override
public void run() {
for (int i = 80; i < 90; i++) {
ThreadLocalTest.integerThreadLocal.set(i);
System.out.println("thread3====>"+ThreadLocalTest.integerThreadLocal.get());
}
}
}
结果可以看到,线程只改变自己线程局部变量的值
thread1====>10
thread1====>11
thread1====>12
thread1====>13
thread1====>14
thread1====>15
thread1====>16
thread1====>17
thread1====>18
thread1====>19
thread3====>80
thread3====>81
thread2====>40
thread2====>41
thread2====>42
thread2====>43
thread3====>82
thread2====>44
thread3====>83
thread2====>45
thread2====>46
thread2====>47
thread2====>48
thread2====>49
thread3====>84
thread3====>85
thread3====>86
thread3====>87
thread3====>88
thread3====>89
ThreadLocal实现原理
1.看源码知道,每个Thread对象内部都维护了一个ThreadLocalMap这样一个ThreadLocal的Map,可以存放若干个ThreadLocal。
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
2.当我们在调用get()方法的时候,先获取当前线程,然后获取到当前线程的ThreadLocalMap对象,如果非空,那么取出ThreadLocal的value,否则进行初始化,初始化就是将initialValue的值set到ThreadLocal中。
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}