并发编程之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值已经不同了。
其根本原因在于每个线程持有副本的引用指向的内存地址都是相同的,详细了解可看内存图。
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