如何将类变量在不同线程间进行隔离?


一 问题的引出

我们来看这样一段代码:

public class Demo1 {
    private static int number = 0;

    public static void main(String[] args) {
        Thread[] threads = new Thread[8];
        for(int i=0;i<8;i++){
            threads[i] = new Thread(()->{
                number += 10;
                System.out.println(Thread.currentThread().getName()+": number="+number);
            });
        }
        for(int i=0;i<8;i++){
            threads[i].start();
        }
    }
}

执行结果:

Thread-0: number=20
Thread-5: number=50
Thread-3: number=40
Thread-1: number=30
Thread-2: number=20
Thread-6: number=70
Thread-4: number=60
Thread-7: number=80

我们定义了一个静态int变量number,初始值为0;
main方法中,我们定义了一个线程数组,每个线程都获取number的值并且+10后打印。
从结果中,我们可以发现:
① 线程的执行是无序的;
② 线程的拿到的值也是相互干扰后的,是被其他线程修改过的;
③ 不同的线程也可能得到相同的值。

如果我们希望number的值相互不干扰的话,怎么做呢?
我们如何将共享资源在不同线程间进行隔离呢?

二 ThreadLocal

我们使用ThreadLocal来解决上述问题,请看如下代码:

public class Demo2 {
    static ThreadLocal<Integer> local = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 0;
        }
    };

    public static void main(String[] args) {
        Thread[] threads = new Thread[8];
        for (int i=0;i<8;i++){
            threads[i] = new Thread(()->{
               int num = local.get();
               //设置local值
               local.set(num += 10);
                System.out.println(Thread.currentThread().getName() + ": number="+num);
            });
        }
        for (int i=0;i<8;i++){
            threads[i].start();
        }
    }
}

执行结果为:

Thread-1: number=10
Thread-0: number=10
Thread-4: number=10
Thread-2: number=10
Thread-7: number=10
Thread-6: number=10
Thread-5: number=10
Thread-3: number=10

可以看到,每个线程获得的值都是0,设置后都为10。
这就是ThreadLocal起到的作用。
protected Integer initialValue() { return 0; } 这里返回的值,是每个线程独有的。

三 ThreadLocal的线程隔离机制

ThreadLocal中的每一个线程,都会存在一个ThreadLocalMap的对象,这个对象是个集合,这个集合里面存储的是key-value形式的数据,key代表的是我们定义的ThreadLocal 对象,value代表的是我要存储在当前线程里的数据。
我们通过代码来印证上述内容:
先来看ThreadLocal源码(部分)

public class ThreadLocal<T> {
	...
    /**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    public T get() {
    	//获取当前线程
        Thread t = Thread.currentThread();
        //通过getMap方法,从当前线程中获取ThreadLocalMap对象
        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();
    }
    /**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {
    	//获取当前线程
        Thread t = Thread.currentThread();
        //返回传入线程的threoadLocals
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            map.set(this, value);
        } else {
            createMap(t, value);
        }
    }
    /**
     * Get the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param  t the current thread
     * @return the map
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
	
    /**
     * Create the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the map
     */
	void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    ......

ThreadLocal的set方法:local.set(num += 10);中,
首先获取当前线程,然后调用getMap方法。
getMap方法返回了传入线程的threadLocals
我们从Thread类可以知道threadLocals的定义:ThreadLocal.ThreadLocalMap threadLocals = null;
它是个ThreadLocalMap类型的变量。
继续看set方法,
如果Thread.threadLocals不为空,直接从将ThreadLocal对象和数据值(num)传入;
如果Thread.threadLocals为空,则为当前线程新创建一个ThreadMap,将ThreadLocal对象和数据值作为参数传入。当前线程当前线程的threadLocals(ThreadLocalMap)、以及local(ThreadLocal<Integer>) 三者的关系如下图所示:
在这里插入图片描述
这幅图也充分表示了不同线程都有一份自己的ThreadMap,里面存放了ThreadLocal local。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值