Java ThreadLocal 理解

ThreadLocal 概念:

ThreadLocal不是用来解决对象共享访问的问题,而主要是提供了保存对象的方法和避免参数传递的方便的对象访问方式。

ThreadLocal并不是一个Thread,而是Thread的局部变量,当使用ThreadLocal维护变量的时候ThreadLocal为每一个使用该变量的线程提供了独立的变量副本,也就是说每一个线程都可以单独改变自己的副本,而不会影响其他线程的副本。

从线程的角度来看目标变量就是当前线程的本地变量,这也就是类名Local的含义。

ThreadLocal的4个方法:

  void set(T value)  将此线程局部变量的当前线程副本中的值设置为指定值

  void remove()  移除此线程局部变量当前线程的值

  protected T initialValue()  返回此线程局部变量的当前线程的初始值

  T get()  返回此线程局部变量的当前线程副本中的值

注意 :默认情况下 initValue(), 返回 null 。线程在没有调用 set 之前,第一次调用 get 的时候, get方法会默认去调用 initValue 这个方法。所以如果没有覆写这个方法,可能导致 get 返回的是 null 。当然如果调用过 set 就不会有这种情况了。但是往往在多线程情况下我们不能保证每个线程的在调用 get 之前都调用了set ,所以最好对 initValue 进行覆写,以免导致空指针异常。

ThreadLocal实现原理:

ThreadLocal是如何做到为每一个线程维护变量的副本呢?让我们来看看ThreadLocal的源码:

public class ThreadLocal<T> {

    public T get() {
        // 获取当前线程
        Thread t = Thread.currentThread();
        // 获取当前线程的threadLocals变量
        ThreadLocalMap map = getMap(t);
        // 从当前线程的threadLocals变量中取得本threadLocal为key的值
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T) e.value;
        }
        return setInitialValue();
    }

    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }


    public void set(T value) {
        // 获取当前线程
        Thread t = Thread.currentThread();
        // 获取当前线程的threadLocals变量
        ThreadLocalMap map = getMap(t);
        // 以本threadLocal为key的保存值到当前线程的threadLocals变量中去
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

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

在ThreadLocal类中有一个map,用于存储没一个线程的变量副本,map中的元素key为线程对象 value为线程副本中的变量值

示例:

package cn.com.example;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by Jack on 2017/2/13.
 */
public class ThreadLocalTest {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 3; i++) {
            executorService.execute(new ThreadTest());
        }

        executorService.shutdownNow();
    }
}


class ThreadTest implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " value=" + ThreadLocalNumber.get());
        }
    }
}

class ThreadLocalNumber {
    private static ThreadLocal<Integer> value = new ThreadLocal<Integer>() {
        protected synchronized Integer initialValue() {
            return 0;
        }
    };

    public static void set() {
        value.set(value.get() + 1);
    }

    public static int get() {
        // 为了测试 直接在get里面 ++
        value.set(value.get() + 1);
        return value.get();
    }
}

结果:

pool-1-thread-3 value=1
pool-1-thread-2 value=1
pool-1-thread-1 value=1
pool-1-thread-2 value=2
pool-1-thread-3 value=2
pool-1-thread-2 value=3
pool-1-thread-1 value=2
pool-1-thread-2 value=4
pool-1-thread-2 value=5
pool-1-thread-3 value=3
pool-1-thread-3 value=4
pool-1-thread-3 value=5
pool-1-thread-1 value=3
pool-1-thread-1 value=4
pool-1-thread-1 value=5

从运行结果得知 各线程都用于各自的Local变量,并各自读写互不干扰。

posted @ 2017-02-13 14:47  Zombie_Xian  阅读(247)  评论(0编辑  收藏  举报