ThreadLocal,顾名思义:线程本地变量。它的作用并不是为了线程安全,线程安全的前提是线程之间共享了变量,而ThreadLocal是把变量的引用或者副本作为value。
现在来看第一个例子:
class Thread1 extends Thread{ StringBuffer s=new StringBuffer("abc"); ThreadLocaltl=new ThreadLocal<>(); @Override public void run() { tl.set(s); System.out.println(tl.get()); s.append("bac"); tl.set(s); System.out.println(tl.get()); }}public class ThreadLocalTest { public static void main(String[] args) { Thread t1=new Thread1(); Thread t2=new Thread1(); t1.start(); t2.start(); }}
输出结果:
abcabcabcabcabcabc
这边个人觉得有两个问题可以提出
1:为什么要用StringBuffer?
2: StringBuffer为什么不设置为static
3:ThreadLocal为什么没设置为static,在很多文章中都是设置为static的。
第一个问题:因为String是java coder最常用的,但是String是常量而不是变量。StringBuffer作为变量同时是线程安全的。
第二个问题:StringBuffer设置为static会使它也成为共享变量,并且ThreadLocal中保存的只是引用,那么在使用ThreadLocal时对StringBuffer进行修改,就会影响另外一个线程了。
第三个问题:其实是应该使用static的!原因是省内存。。。
class Thread1 extends Thread{ static StringBuffer s=new StringBuffer("abc"); static ThreadLocaltl=new ThreadLocal<>(); @Override public void run() { tl.set(s); System.out.println(tl.get()); s.append("bac"); tl.set(s); System.out.println(tl.get()); }}public class ThreadLocalTest { public static void main(String[] args) { Thread t1=new Thread1(); Thread t2=new Thread1(); t1.start(); t2.start(); }}
进行修改代码后的结果 abcabcbacabcbacabcbacbac
现在才是重头戏,分析源码
我们来看ThreadLocal的set()方法,这个方法是将value插入到map中的,But?
1:key是什么--> 是ThreadLocal
2:插入到哪里?-->Thread.ThreadLocalMap
ThreadLocal.ThreadLocalMap threadLocals = null;//这是Thread类中的一行代码
(ThreadLocal类,以下全是) public void set(T value) { Thread t = Thread.currentThread(); 获取当前线程 ThreadLocalMap map = getMap(t); 获取ThreadLocalMap。因为Thread类中有这个变量,但是它并没有进行初始化(第二个问题) if (map != null) 如果当前线程中的ThreadLocalMap!=null,即已经指向一个对象 map.set(this, value); map就进行插入,this指的便是ThreadLocal这个对象,value就是插入的值(第一个问题),这边是会覆盖的。 else 具体的插入就不看了,如果想看,可以看我即将写的HashMap的源码解析 createMap(t, value); 如果为空,进行创建。见下一篇代码
}
void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
public T get() { Thread t = Thread.currentThread(); //获取当前线程 ThreadLocalmap = getMap(t); //获取map if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); //通过ThreadLocal这个key从ThreadLocalMap的内部实现获取Entry,见下一篇代码 if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; //获取值 return result; } } return setInitialValue(); }
private Entry getEntry(ThreadLocal key) { int i = key.threadLocalHashCode & (table.length - 1); 类似hashMap,通过取模作为哈希函数 Entry e = table[i]; if (e != null && e.get() == key) 判断key是否相等,这边没有进行遍历,说明解决冲突的方法不是链地址法 return e; else return getEntryAfterMiss(key, i, e); }
引用一段话:来自http://blog.sina.com.cn/s/blog_d459b83c0102ve6s.html
总之,ThreadLocal不是用来解决对象共享访问问题的,而主要是提供了保持对象的方法和避免参数传递的方便的对象访问方式。归纳了两点:
1。每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。 2。将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象,避免了将这个对象作为参数传递的麻烦。