详解Java中的ThreadLocal、ThreadLocalMap和Thread之间的关系

原创 2015年11月18日 02:49:49

每个ThreadLocal实例都有一个唯一的threadLocalHashCode(这个值将会用于在ThreadLocalMap中找到ThreadLocal对应的value值),它是通过静态变量nextHashCode和HASH_INCREMENT进行计算的,其中nextHashCode 和HASH_INCREMENT 的定义如下

private static AtomicInteger nextHashCode = new AtomicInteger();

private static final int HASH_INCREMENT = 0x61c88647;

nextHashCode()方法如下

private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT); 
}

每个Thread中都有一个threadLocals字段,其定义如下:

ThreadLocal.ThreadLocalMap threadLocals = null;

这个字段在创建线程时为null,当调用ThreadLocal中的setInitialValue()和set(T value)方法时会创建ThreadLocalMap对象,这两个方法内部都是调用createMap方法

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

 }

setInitialValue()方法中设置的firstValue值是通过initialValue()方法得到的,默认的initialValue()方法返回值是null,如下所示:

protected T initialValue() {
        return null;
}

在我们自己使用时,因此通常需要override此方法。

ThreadLocalMap类中有一个静态内部类Entry,ThreadLocal实例和对应的值都是存放在entry元素中的,类定义如下:

static class Entry extends WeakReference<ThreadLocal> {
        
            Object value;


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

Entry继承了WeakReference类,其中WeakReference类是Reference类的子类,其构造方法就是调用的Reference类的构造方法:

Reference(T referent) {
this(referent, null);
}

Reference(T referent, ReferenceQueue<? super T> queue) {
this.referent = referent;
this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}

Reference中有个get()方法:

public T get() {
return this.referent;

}

其返回的即是构造方法中传入的ThreadLocal实例

关于Reference类以及他的子类,不在本篇的讨论中,将在后续博客中进行详解

现在分析下ThreadLocal中设置值的方法,设置值的具体操作都是调用

 map.set(this, value);

其内部调用的是ThreadLocalMap对象的

private void set(ThreadLocal key, Object value)方法

此方法大致说明下,不单独细细展开说明了,首先通过位操作(这时本文一开始提到的threadLocalHashCode就大显身手了)找到ThreadLocal实例在private Entry[] table中的index,然后将table[index]中的值设为新的new Entry(key, value)

取得值的方法public T get(),里面有个获取entry的代码

ThreadLocalMap.Entry e = map.getEntry(this);

具体如下:

private Entry getEntry(ThreadLocal key) {
    int i = key.threadLocalHashCode & (table.length - 1);
    Entry e = table[i];
    if (e != null && e.get() == key)
        return e;
    else
        return getEntryAfterMiss(key, i, e);

}

首先进行位运算获取index位置,然后检查该位置的值是否存在,已经值是不是需要的key,e.get方法即是上文所说的返回对应的ThreadLocal实例,如果是的话就直接返回entry,如果没有直接在hash槽中发现对应的ThreadLocal实例,进行寻找。

下面来看看具体的使用

例子1:

public class MyThreadLocal extends ThreadLocal<Long>{

Long value = new Long(1);

@Override
public Long initialValue(){
return new Long(1);
}


public static void main(String[] args) {



MyThreadLocal longThreadLocal = new MyThreadLocal();

new Thread(new LongThread(longThreadLocal, new Long(2))).start();
new Thread(new LongThread(longThreadLocal, new Long(5))).start();
new Thread(new LongThread(longThreadLocal, new Long(8))).start();
}


}


public class LongThread implements Runnable {


private ThreadLocal<Long> local;
private Long value;

public LongThread(ThreadLocal<Long> local,Long value){
this.local = local;
this.value = value;
}

@Override
public void run() {


String threadName=Thread.currentThread().getName();
try {
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(threadName+":"+local.get());
System.out.println(threadName+": value "+value);
local.set(new Long(value+local.get()));
System.out.println(threadName+": final "+local.get());
}


}

运行结果如下:

Thread-2:1
Thread-2: value 8
Thread-2: final 9
Thread-1:1
Thread-1: value 5
Thread-1: final 6
Thread-0:1
Thread-0: value 2
Thread-0: final 3


例子2:

public class ListThreadLocal extends ThreadLocal<List<String>> {


List<String> tudouList=new ArrayList<String>();


@Override
public List<String> initialValue(){
tudouList.add("a");
tudouList.add("b");
System.out.println("List size :"+tudouList.size());
return tudouList;
}

public static void main(String[] args) {

ListThreadLocal local = new ListThreadLocal();

new Thread(new ListThread(local)).start();
new Thread(new ListThread(local)).start();
new Thread(new ListThread(local)).start();
}


}


public class ListThread implements Runnable {


private ThreadLocal<List<String>> local = new ThreadLocal<List<String>>();

public ListThread(ThreadLocal<List<String>> local){
this.local = local;
}

@Override
public void run() {
String threadName = Thread.currentThread().getName();
try {
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(threadName+":"+local.get());
local.get().add(Thread.currentThread().getName());
System.out.println(threadName + ": final " + local.get());
}


}

运行结果:

List size :2
Thread-0:[a, b]
Thread-0: final [a, b, Thread-0]
List size :5
Thread-1:[a, b, Thread-0, a, b]
Thread-1: final [a, b, Thread-0, a, b, Thread-1]
List size :8
Thread-2:[a, b, Thread-0, a, b, Thread-1, a, b]


可以看到,传递引用类型的话,还是会改变引用的值的。
Thread-2: final [a, b, Thread-0, a, b, Thread-1, a, b, Thread-2]

相关文章推荐

ThreadLocal源码解析,以及ThreadLocal、ThreadLocalMap、Thread 三者之间的关系

ThreadLocal、ThreadLocalMap、Thread 三者之间的关系 ThreadLocalMap 是 ThreadLocal 的内部类,Thread 中有个 ThreadLocal...

彻底理解ThreadLocal

ThreadLocal是什么 早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁...
  • lufeng20
  • lufeng20
  • 2014年04月22日 16:59
  • 307060

ThreadLocal,ThreadLocalMap,Thread 的相互关系以及设计原理分析

一句话,ThreadLocal并不是把线程作为key,值作为value的类似一种HashMap的东西。而是每个Thread里面都有一个ThreadLocalMap的集合,ThreadLocal只是操作...

Thread中ThreadLocal.ThreadLocalMap映射表的初始化

ThreadLocal的目的和作用:   用于实现线程内的数据共享,即对于相同的程序代码,多个模块在同一个线程中运行时要共享一份数据,而在另外线程中运行时又共享另外一份数据。   每个线程调用全局...

ThreadLocal、ThreadLocalMap弱引用key

ThreadLocal类为每一个线程都维护了自己独有的变量拷贝。每个线程都拥有了自己独立的一个变量,竞争条件被彻底消除了,那就没有任何必要对这些线程进行同步,它们也能最大限度的由CPU调度,并发执行。...
  • enetor1
  • enetor1
  • 2014年09月02日 15:19
  • 1407

ThreadLocal原理解析(2):ThreadLocalMap源码解析

在上一篇文章【ThreadLocal原理解析(1):数据存取】中,我们介绍了`ThreadLocal`读取数据的过程及原理。我们知道,`ThreadLocal`将变量的各个副本值保存在各个线程`Thr...

通向架构师的道路(第七天)之漫谈使用ThreadLocal改进你的层次的划分

一、什么是ThreadLocal早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁...

java多线程并发控制之ThreadLocal

下面是ThreadLocal的测试代码,更多信息请参考注释 package com.jadyer.thread.local;      import java.util.Ra...

获取两个List集合中的不相同的对象

import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; ...

ThreadLocale理解和对WeakReference的运用

理论基础看其他收录的文章,这里记录的是自己的理解。 每个Thread都有一个成员变量ThreadLocal.ThreadLocalMap threadLocals = null;默认为空。 Threa...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:详解Java中的ThreadLocal、ThreadLocalMap和Thread之间的关系
举报原因:
原因补充:

(最多只允许输入30个字)