我们常说,synchronized采取的是“以时间换空间”的策略,本质上是对关键资源上锁,让大家排队操作。ThreadLocal则恰恰相反,采取的是“以空间换时间”的思路,为每个使用该变量的线程提供独立的变量副本,在本线程内部,它相当于一个“全局变量”,可以保证本线程任何时间操纵的都是同一个对象。
看了一些理解ThreadLocal的文章,归纳了如下两点:
一,ThreadLocal 不是用来解决共享对象的多线程访问问题的,主要是提供了避免参数传递的资源访问方式。每个线程都有一个自己的ThreadLocalMap(Thread类的成员变量),可以将线程需要的对象保持到其中,各管各的互不干扰,线程可以正确访问到自己的对象。一般情况下,通过ThreadLocal.set() 到线程中的对象,是该线程自己要使用的对象(例如session),其他线程不需要访问,也访问不到。
二,ThreadLocal使各线程保持各自独立的一个对象,是通过每个线程对象的new操作来实现的。将一个共用的ThreadLocal静态实例作为key(如图),将不同对象的引用保存到不同线程的map中,然后在线程执行的各处通过ThreadLocal.get()方法获取自己线程保存的对象,避免了将这个对象作为参数传递的麻烦。
阅读源码:可以看到ThreadLocal是Thread类的成员变量(ThreadLocalMap是ThreadLocal的静态内部类),并且Thread中并没有对外提供访问成员变量的方法。
而在ThreadLocal类中提供了访问变量ThreadLocalMap 的get()方法:通过获取当前线程的方式,从而保证每个线程访问的都是自己的ThreadLocalMap。
ThreadLocal的简单应用
建立ThreadLocal容器对象,对需要保存的对象进行封装,并提供相应的get/set方法(全部为static)
1. 创建一个Context类,维护我们要传递的字段。
@Data
public class Context {
/**
* 私房钱
*/
private String money = null;
}
2. 建立ThreadLocal容器对象,对需要保存的对象进行封装,并提供相应的get/set方法(全部为static)
public class MyThreadLocal {
public static final ThreadLocal myThreadLocal = new ThreadLocal();
public static void set(Context context) {
myThreadLocal.set(context);
}
public static Context get() {
return (Context) myThreadLocal.get();
}
}
测试我就不贴了,感兴趣的同学可以自己试一试。