2

【概念

ThreadLocal类用来存放线程的局部变量,每个线程都有自己的局部变量彼此之间不共享。TheadLocal<T>主要有以下三个方法:

  1. public T get():返回当前线程的局部变量。
  2. protected T initValue():返回当前线程的局部变量初始值。默认情况下 initValue(), 返回 null 。线程在没有调用 set 之前,第一次调用 get 的时候, get 方法会默认去调用 initValue 这个方法。所以如果没有覆写这个方法,可能导致 get 返回的是 null 。当然如果调用过 set 就不会有这种情况了。
  3. public void set(T value):设置当前线程的局部变量。

ThreadLocal是如何做到为每一个线程提供单独的局部变量呢?实际上在ThreadLocal类中有一个Map缓存,用于存储每一个线程的局部变量。Map中元素的键为线程对象,而值对应线程的变量副本。

 /**
         * Construct a new map initially containing (firstKey, firstValue).
         * ThreadLocalMaps are constructed lazily, so we only create
         * one when we have at least one entry to put in it.
         */
        ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }

【工作流程

1.set()的时候:

 public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

我们可以看见ThreadLocalMap是从currentThread中获取的,也就是说这些局部变量真正存储在currentThread中:

void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

2.get()的时候:

 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();
    }

从currentThread中获取到了ThreadLocalMap,然后再从中获取value。如果没有值,则调用setInitialValue()。

【如何使用

package ThreadLocal;

import java.util.Random;

import static ThreadLocal.ThreadLocalTest.localSetter;

/**
 * 在每个线程中单独存放一个值然后再获取
 */
public class ThreadLocalTest {

    static ThreadLocal localSetter = new ThreadLocal();

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            MyThread t = new MyThread();
            t.start();
        }
    }
}
class MyThread extends Thread{

    private static Random random = new Random();

    @Override
    public void run(){
        //现在当前线程中存入一个随机变量 v1
        int randomVar = random.nextInt(100);
        localSetter.set(randomVar);
        System.out.println(getName() + " random is " + randomVar);
        //获取当前线程的随机变量 v2
        //v1 永远等于 v2
        System.out.println(getName() + " localVar is " + localSetter.get());
    }
}

【spring与ThreadLocal

spring中的bean大多都是单例的,但是我们应用又是支持并发的,所以将各个service,dao存放在ThreadLocal中是一个明智的做法。但是需要注意如果bean中包含共享变量,spring是没有做任何的安全处理的。

【synchronized与ThreadLocal

ThreadLocal以空间换取时间,提供了一种非常简便的多线程实现方式。
ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区别:
synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本。Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。


极品公子
221 声望43 粉丝

山不向我走来,我便向山走去。