1、介绍
概念:ThreadLocal叫做线程本地变量,当某个线程访问该变量的时候,会在当前线程内部创建一个该变量的副本,在当前线程中对该变量的操作(增删改查)都是针对的副本变量,不会对其它线程造成影响。
作用:使共享变量在多线程间隔离操作,相互不影响,达到线程隔离的效果。
2、和synchronized异同
synchronized
与ThreadLocal
都是为了解决并发编程中可能出现的线程安全问题,二者采取的完全相反的解决方式,应用场景不同。
synchronized
是采用锁机制,使操作共享变量的代码块或方法在同一时间段只能被一个线程访问。
ThreadLocal
是采用隔离机制,直接将要操作的共享变量复制到线程私有,使每个线程访问到的不是同一个变量。
3、使用示例
/**
* 代码
*/
public class Demo {
public static void main(String[] args) throws InterruptedException {
Content content = new Content();
new Thread(()->{
System.out.println("线程名称:"+Thread.currentThread().getName());
System.out.println(content.getLocalVal().get());
content.getLocalVal().set("aaa");
System.out.println(content.getLocalVal().get());
}).start();
TimeUnit.SECONDS.sleep(3);
System.out.println("线程名称:"+Thread.currentThread().getName());
System.out.println(content.getLocalVal().get());
content.getLocalVal().set("bbb");
System.out.println(content.getLocalVal().get());
}
}
class Content {
private ThreadLocal<String> localVal = new ThreadLocal<>();
public ThreadLocal<String> getLocalVal() {
return localVal;
}
}
/**
* 运行结果
*
* 线程名称:Thread-0
* null
* aaa
* 线程名称:main
* null
* bbb
*/
//可以看到,Thread-0线程对变量localVal的操作,并没有影响到主线程的操作。
4、实现原理
每个线程内部都有一个ThreadLocalMap
对象,其中的键为ThreadLocal
,值就是我们设置的value,查看源码。
ThreadLocal.set()源码
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
//获取当前线程。
Thread t = Thread.currentThread();
//获取当前线程的ThreadLocalMap对象。
ThreadLocalMap map = getMap(t);
if (map != null)
//如果当前线程的ThreadLocalMap对象不为null,将value设置到当前线程。
map.set(this, value);
else
//若ThreadLocalMap为null,则创建ThreadLocalMap对象并赋值。
createMap(t, value);
}
ThreadLocal.get()源码
/**
* 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对象。
ThreadLocalMap map = getMap(t);
if (map != null) {
//根据当前线程获取ThreadLocalMap的value。
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
5、注意事项
在使用完ThreadLocal时,及时调用它的remove()
方法,避免内存泄露风险。
public class Demo {
public static void main(String[] args) throws InterruptedException {
Content content = new Content();
new Thread(()->{
System.out.println("线程名称:"+Thread.currentThread().getName());
System.out.println(content.getLocalVal().get());
content.getLocalVal().set("aaa");
System.out.println(content.getLocalVal().get());
content.getLocalVal().remove();
}).start();
TimeUnit.SECONDS.sleep(3);
System.out.println("线程名称:"+Thread.currentThread().getName());
System.out.println(content.getLocalVal().get());
content.getLocalVal().set("bbb");
System.out.println(content.getLocalVal().get());
content.getLocalVal().remove();
}
}
class Content {
private ThreadLocal<String> localVal = new ThreadLocal<>();
public ThreadLocal<String> getLocalVal() {
return localVal;
}
}