ThreadLocal
标签:Java并发

ThreadLocal

ThreadLocal是一个线程的局部变量,即只有当前线程可以访问,故也是线程安全的。

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 getMap(Thread t) {
    return t.threadLocals;
}

ThreadLocal.ThreadLocalMap threadLocals = null;

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

先获得当前线程对象,然后获得ThreadLocalMap,如果map不为空的话,就将value设置进去,通过下面的代码,我们可以看见ThreadLocalMap其实就是ThreadLocal的一个内部类,当我们创建一个ThreadLocalMap的时候就是以当前这个线程为对象,要存储的值为value的。

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

 static class ThreadLocalMap {

        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
 }

前面都是一样的,获取当前线程的ThreadLocalMap,然后再获取其中的Entry,我们就可以从Entry中获取到我们设置的value了。

下面是Thread的exit方法:

private void exit() {
    if (group != null) {
        group.threadTerminated(this);
        group = null;
    }
    /* Aggressively null out all reference fields: see bug 4006245 */
    target = null;
    /* Speed the release of some of these resources */
    threadLocals = null;
    inheritableThreadLocals = null;
    inheritedAccessControlContext = null;
    blocker = null;
    uncaughtExceptionHandler = null;
}

我们可以看见在线程退出的时候,Thread通过设置对象等于null来加速资源的清理,如果我们在线程池中,线程是复用的,那么一些大对象被放到了ThreadLocal中,使用几次就不再使用的话,而它又不能被回收,将会导致内存的泄露。

ThreadLocal对于性能的提升,下面是一个生成随机数的例子:

public class RandomDemo {
    public static final int GEN_COUNT=10000000;
    public static final int THREAD_COUNT=4;
    static ExecutorService executorService= Executors.newFixedThreadPool(THREAD_COUNT);
    public static Random rnd=new Random(123);

    public static ThreadLocal<Random> trand=new ThreadLocal<Random>(){
        @Override
        protected Random initialValue() {
            return new Random(123);
        }
    };

    public static class RndTask implements Callable<Long>{
        private int mode=0;

        public RndTask(int mode) {
            this.mode = mode;
        }

        public Random getRandom(){
            if (mode==0){
                return rnd;
            }else if (mode==1){
                return trand.get();
            }else {
                return null;
            }
        }

        @Override
        public Long call() throws Exception {
            long b=System.currentTimeMillis();
            for (long i = 0; i <GEN_COUNT ; i++) {
                getRandom().nextInt();
            }
            long e=System.currentTimeMillis();
            System.out.println(Thread.currentThread().getName()+" "+(e-b));
            return e-b;
        }
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Future<Long>[] futs=new Future[THREAD_COUNT];
        for (int i = 0; i < THREAD_COUNT; i++) {
            futs[i]=executorService.submit(new RndTask(0));
        }
        long totaltime=0;
        for (int i = 0; i < THREAD_COUNT; i++) {
            totaltime+=futs[i].get();
        }
        System.out.println("多线程访问同一个Random实例"+totaltime);
        for (int i = 0; i < THREAD_COUNT; i++) {
            futs[i]=executorService.submit(new RndTask(1));
        }
        totaltime=0;
        for (int i = 0; i < THREAD_COUNT; i++) {
            totaltime+=futs[i].get();
        }
        System.out.println("使用ThreadLocal包装的实例"+totaltime);
        executorService.shutdown();
    }
}

我们采用ThreadLocal为每一个线程分配单独的对象,最后结果为:

pool-1-thread-2 3032
pool-1-thread-1 3117
pool-1-thread-3 3125
pool-1-thread-4 3128
多线程访问同一个Random实例12402
pool-1-thread-4 124
pool-1-thread-2 130
pool-1-thread-3 130
pool-1-thread-1 138
使用ThreadLocal包装的实例522
  • 3 min read

CONTRIBUTORS


  • 3 min read