ThreadLocal

早期设计

image.png

早期设计的问题

其拥有者为ThreadLocal,每一个ThreadLoca1实例,拥有一个Map实例。

问题1:浪费空间

如果线程数量多,则ThreadLocalMap存储”Key-Value” entry的数量也多。一般情况下,程序的ThreadLoca 1实例会比较少,而线程数较多,所以,这种涉及方案,比较浪费空间。

问题2:存活周期太长

在Thread (线程)实例销毁后,ThreadLoca 1实例内部的Th readLoca lMap还是存在的。ThreadLocal本来是给Thread 做线程隔离用的,现在Thread 不需要隔离了,但是ThreadLocal还在。

JDK1.8的ThreadLocal

现在ThreadLocalMap的归属是线程了,由线程去持有ThreadLocal变量
image.png

  1. import java.util.concurrent.atomic.AtomicInteger;
  2. public class ThreadId {
  3. // Atomic integer containing the next thread ID to be assigned
  4. private static final AtomicInteger nextId = new AtomicInteger(0);
  5. // Thread local variable containing each thread's ID
  6. private static final ThreadLocal<Integer> threadId =
  7. new ThreadLocal<Integer>() {
  8. @Override protected Integer initialValue() {
  9. return nextId.getAndIncrement();
  10. }
  11. };
  12. // Returns the current thread's unique ID, assigning it if necessary
  13. public static int get() {
  14. return threadId.get();
  15. }
  16. }


  • 本质上,ThreadLocal是通过空间来换取时间,从而实现每个线程当中都会有一个变量的副本,这样每个线程就都会操作该副本,从而完全规避了多线程的并发问题,不再需要同步变量。
  • ThreadLocal的实例要声明为static类型,表示全局的含义,使当前类所有实例都可以访问到这个ThreadLocal变量。
  1. public static void main(String[] args) {
  2. ThreadLocal<String> threadLocal = new ThreadLocal();
  3. threadLocal.set("hello world");
  4. System.out.println(threadLocal.get());
  5. threadLocal.set("welcome");
  6. System.out.println(threadLocal.get());
  7. }

ThreadLocal.get()

image.png


Thread类里面的threadLocals

  1. ThreadLocal.ThreadLocalMap threadLocals = null;

Thread类里面有一个**ThreadLocalMap类型的成员变量 threadLocals**


ThreadLocalMap与Entry

  • ThreadLocalMap 是 ThreadLocal 的内部类
  • Entry 是 ThreadLocalMap的内部类


:::tips Entry的key是ThreadLocal,值是我们要存的对象。
可以发现,这个对象并不是存储在ThreadLocal的对象中,而是将T**h**readLocal实例作为key目标Object作为value,存储到Entry中,然后存储到当前线程的threadLocals里面! :::

image.png


ThreadLocal与Synchonized的比较

  1. ThreadLocalSynchonized都用于解决多线程并发访问。
  • synchronized是利用锁的机制,使变量或代码块在某一时该仅仅能被一个线程访问。
  • ThreadLocal为每个线程都提供了变量的副本,使得每个线程在某一时间访问到的并非同一个对象,这样就隔离了多个线程对数据的数据共享。