在网上看了很多关于threadlocal的文章,感觉大部分说的都是不明其意,关于threadlocal的作用也是众说纷纭,但最多的说法是threadlocal的作用是用于解决多线程并发问题,但自己经过测试,发现并不是那么简单,所以记录下来以供自己日后复习和参考。
首先要确定的一点是theadlocal的出现并不是为了解决并发问题的,他的作用简单来讲应该是实现线程独有的全局变量这样一个概念。ThreadLocal之所以能使得各线程有各自独立的一个对象,不是通过对什么对象的拷贝或副本来实现的,而是通过每个线程中的new 对象 的操作来创建的对象(initialValue方法),每个线程创建一个,才能保持各线程之间互不干扰。
threadlocal的set方法如下
/**
* 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);
}
createMap方法
/**
* 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
* @param map the map to store.
*/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
可以从上面看到完全没有什么做拷贝或是副本的操作,所以如果ThreadLocal.set()进去的东西本来就是多个线程共享的同一个对象,那么多个线程的ThreadLocal.get()取得的还是这个共享对象本身,还是有并发访问问题。 具体验证如下
package EX04;
import java.util.HashMap;
public class Test {
static ThreadLocal<HashMap<String,String>> stringLocal = new ThreadLocal<HashMap<String,String>>();
static HashMap<String,String> map = new HashMap<String,String>();
public static void main(String[] args) throws InterruptedException {
stringLocal.set(map);
System.out.println("main >>> " +stringLocal.get().get("key"));
stringLocal.get().put("key", "value1");
Thread thread1 = new Thread() {
public void run() {
stringLocal.set(map);
System.out.println("thread1 >>> " + stringLocal.get().get("key"));
stringLocal.get().put("key", "value2");
};
};
thread1.start();
thread1.join();
System.out.println("main >>> " + stringLocal.get().get("key"));
}
}
输出:
main >>> null
thread1 >>> value1
main >>> value2
可以看到同一个static变量map,主线程修改可以影响thread1线程,thread1线程也可以影响主线程,验证结束。
最后重申,threadlocal的创建不是为了解决并发问题,而是为了作为线程内的全局变量去使用,比如数据库操作,如果一个事务中涉及多个 DAO 操作,而如果这些DAO中的Connection都是独立创建的话,就没有办法完成一个事务。但是如果放在 ThreadLocal 中就可以解决这个问题,当然他额外的好处就是,在使用过程中,别的线程(用户)也无法调用或者修改这个线程里connection对象。