并发编程之ThreadLocal

并发编程之ThreadLocal

首先说明一下啊,ThreadLocal其实不应该放在并发编程系列,只是在不知道放到哪合适

介绍

ThreadLocal翻译为"本地线程",但是这个类的取名似乎词不达意了。也许使用ThradLocalVariable(线程本地变量)可能更合适。

JDK源码描述:

ThreadLocal类用来提供线程内部的局部变量。

这种变量在多线程环境下访问(通过get或set方法访问)时能保证各个线程里的变量相对独立于其他线程内的变量。

ThreadLocal实例通常来说都是private static类型的,用于关联线程和线程的上下文。

1、每个线程都有自己的局部变量(该变量对其它线程不可见)

每个线程都有一个独立于其他线程的上下文来保存这个变量,一个线程的本地变量对其他线程是不可见的

2、拥有独立于变量的初始化副本

ThreadLocal可以给一个初始值,而每个线程都会获得这个初始化值的一个副本,这样才能保证不同的线程都有一份拷贝

3、状态与某一线程相关联

重点:ThreadLocal不是用来解决共享变量问题的,不是为了协调线程同步而存在,而是为了方便每个线程处理自己的状态而引入的一种机制。

ThreadLocal案列解析理解

IntegerThread Code

/**
 * 
 * @类名: IntegerThread
 * @Time 2016年8月18日 下午2:01:12
 * @功能描述:基本数据类型的ThreadLocal
 * @author xuyi3
 * @春风十里不如你
 *
 */
public class IntegerThread implements Runnable
{

    // 基本数据类型的ThreadLocal使用
    private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>()
    {
        protected Integer initialValue()
        {
            return 0;
        }

    };

    public void run()
    {
        // 修改threadLocal的值
        int num = threadLocal.get();
        for (int j = 0; j < 10; j++)
        {
            num++;
            threadLocal.set(num);
        }
        System.out.println(Thread.currentThread().getName() + "---" + num);
    }

}

IndexThread Code

/**
 * 
 * @类名: IndexThread
 * @Time 2016年8月18日 下午2:02:06
 * @功能描述: 引用数据类型的ThreadLocal
 * @author xuyi3
 * @春风十里不如你
 *
 */
public class IndexThread implements Runnable
{

    // 定义ThreadLocal<Index> 变量
    private static ThreadLocal<Index> threadLocal = new ThreadLocal<Index>()
    {
        Index index = new Index();

        protected Index initialValue()
        {
            return index;
            // return new Index();
            // return index 和return new Index()结果是完全不同的。

            // 备注:使用ThreadLocal修饰引用数据类型时要特别注意。
        }
    };

    public void run()
    {
        for (int i = 0; i < 1000; i++)
        {
            threadLocal.get().incr();
        }
        System.out.println(Thread.currentThread().getName() + "---" + threadLocal.get().getNum());
    }
}

Main Code

public class Main
{
    public static void main(String[] args)
    {
        // ThreadLocal<Integer>案列展示
        IntegerThread integerThread = new IntegerThread();
        new Thread(integerThread).start();
        new Thread(integerThread).start();
        new Thread(integerThread).start();
        new Thread(integerThread).start();
        new Thread(integerThread).start();

        // ThreadLocal<Index>案列展示
        IndexThread indexThread = new IndexThread();

        new Thread(indexThread).start();
        new Thread(indexThread).start();
        new Thread(indexThread).start();
        new Thread(indexThread).start();
        new Thread(indexThread).start();
    }
}

分析IndexThread中initialValue方法,如果初始值返回的是index而不是new Index()会出现的同步问题。

t1时刻启动线程1,
t2时刻线程1其将index的num值增加为3,此时启动线程2,线程2取出的index副本,就是改变之后的index(num为3),
和线程1开始取出的副本index的num值已经不同了。


其根本原因在于每个线程持有副本的引用指向的内存地址都是相同的,详细了解可看内存图。

返回index引用的内存分析

返回index引用的内存分析


返回new Index()时内存分析

返回new Index()时内存分析

ThreadLocal源码分析

/**
*ThreadLocal类用来提供线程内部的局部变量。
这种变量在多线程环境下访问(通过get或set方法访问)时能保证各个线程里的变量相对独立于其他线程内的变量。 
ThreadLocal实例通常来说都是private static类型的,用于关联线程和线程的上下文。
*/
public class ThreadLocal<T> {

    /**
     * 
     * 返回此线程局部变量的当前线程的“初始值”。
     *
     * 该实现返回 null;如果程序员希望线程局部变量具有 null 以外的值,则必须为 ThreadLocal 
     * 创建子类,并重写此方法。通常将使用匿名内部类完成此操作。
     *
     */
    protected T initialValue() {
        return null;
    }

    /**
     * 返回此线程局部变量的当前线程副本中的值。如果变量没有用于当前线程的值,则先将其初始化为调用 
     * initialValue() 方法返回的值。     * 
     */
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }

    /**
     * 将此线程局部变量的当前线程副本中的值设置为指定值。大部分子类不需要重写此方法,它们只依靠
     * initialValue() 方法来设置线程局部变量的值。
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }


    //移除此线程局部变量当前线程的值
     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

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

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

    //ThreadLocalMap值得单独分析
    static class ThreadLocalMap {

            // ... 

    }
}

总结

ThreadLocal的作用是提供线程内的局部变量。

这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。

其实注意看框架源码注意的话,就会发现其实蛮多地方是使用了ThreadLocal的,通常使用private static 修饰。

ThreadLocal千万不要误用,误将其视为解决并发同步的手段。

参考

1、https://www.ibm.com/developerworks/cn/java/j-5things15/
2、https://www.zhihu.com/question/23089780
3、http://my.oschina.net/clopopo/blog/149368

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值