您的位置:首页 > 其它

Threadlocal理解

2011-06-10 16:55 260 查看
今天看了ThreadLocal机制,写一点读书笔记。

首先解释下ThreadLocal的含义,

ThreadLocal很容易让人望文生义,想当然地认为是一个“本地线程”。其实,ThreadLocal并不是一个Thread,也不是Thread的局部变量,其实他是作为Thread内部一个Map的key值来存在的。

每个线程内部都会有一个这样的Map,通过Thread.java的源代码我们可以看到一个类型为ThreadLocal.ThreadLocalMap的变量 threadLocals,线程每次被new出来就会初始化这个

threadLocals变量,(我看过网上很多文章说ThreadLocals的键是线程本身,值是变量的副本,从源代码来看不是这样的。请看下面解释,迎指正)

(以下代码来自Thread.java)

/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;


我们再看下面一个例子(来自http://zhxing.iteye.com/blog/306070)

SequenceNumber
package com.baobaotao.basic;
public class SequenceNumber {
//①通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值
private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>(){
//请注意,SequenceNumber为我们真正要使用的变量进行的封装,同时在内部声明了一个静态的ThreadLocal对象,而我们对变量的取值实际上都是通过ThreadLocal里面的get和set方法来的,这就是我们要特别注意的地方。
public Integer initialValue(){
return 0;
}
};
//②获取下一个序列值
public int getNextNum(){
seqNum.set(seqNum.get()+1);
return seqNum.get();
}
public static void main(String[] args)
{
SequenceNumber sn = new SequenceNumber();
//③ 3个线程共享sn,各自产生序列号
TestClient t1 = new TestClient(sn);
TestClient t2 = new TestClient(sn);
TestClient t3 = new TestClient(sn);
t1.start();
t2.start();
t3.start();
}
private static class TestClient extends Thread
{
private SequenceNumber sn;
public TestClient(SequenceNumber sn) {
this.sn = sn;
}
public void run()
{
for (int i = 0; i < 3; i++) {//④每个线程打出3个序列值
System.out.println("thread["+Thread.currentThread().getName()+ "] sn["+sn.getNextNum()+"]");
}
}
}
}


在Main函数中,我们声明了三个

TestClient 变量t1,t2,t3

,在run方法中调用了sn.getNextNum(),而getNextNum()这个方法就调用了sn这个对象内部ThreadLocal对象的get和set方法。我们看看ThreadLocal对象内部的get和set方法的源代码:

(以下代码来自

ThreadLocal.java)

/**
* 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();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
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();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}


从set方法的代码中我们可以看到,当你调用sn.getNextNum()方法的时候,这个方法内部先调用set方法然后是get方法,从ThreadLocal源代码的set方法来看:首先得到当前线程变量t,然后得到t的ThreadLocalMap对象,然后可能会调用map.set(this, value),可以看出this就代表ThreadLocal对象,这是肯定的,因为set方法是属于ThreadLocal这个类的。继续追踪ThreadLocalMap的set方法源代码,可以看出是把ThreadLocal这个对象作为key的。在这个列子中就是sn或者seqNum 这个对象。而value值就不用再解释了。

每一个使用了

SequenceNumber类型对象的线程,在调用getNextNum()方法的时候就会把变量生成一个副本,然后以SequenceNumber里面的ThreadLocal的对象为key,把变量副本作为value放在当前这个线程的ThreadLocalMap对象里面。而每个线程都有自己独立的ThreadLocalMap对象。当某个线程要取这个ThreadLocal所给予这个线程的变量时候,就通过ThreadLocal的get方法来取,研究get方法的源代码可知以上分析是正确的。

ThreadLocal为解决多线程程序的并发问题提供了一种新的思路, 但是ThreadLocal是为每个线程都拷贝一份变量的副本,而不是让多个线程使用锁的方式进行并发控制去访问一个共享的变量。使用锁的的方式进行同步意味着,每个线程之间需要通过这个变量进行通信,也就是说这个变量的值或者状态最好应该是可变的,如果不是可变的就不需要使用锁的方式进行线程同步。使用线程本地变量可以更好地对这种行为进行同步。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: