一、简介
ThreadLocal的引入,在多线程情况下,实现线程变量的共享,但是又不相互影响,也就是所说的线程隔离。
下面是非线程安全的方法实现
1 package com.volshell.threads; 2 3 import java.util.Date; 4 import java.util.concurrent.TimeUnit; 5 6 public class UnsafeTask implements Runnable { 7 private Date startDate; 8 9 @Override 10 public void run() { 11 // TODO Auto-generated method stub 12 startDate = new Date(); 13 System.out.println("Start thread :" + Thread.currentThread().getId() 14 + ": " + startDate); 15 16 try { 17 TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10)); 18 } catch (InterruptedException e) { 19 // TODO Auto-generated catch block 20 e.printStackTrace(); 21 } 22 23 System.out.println("Thread finished :" + Thread.currentThread().getId() 24 + "--" + startDate); 25 } 26 27 }
测试代码:
package com.volshell.threads.test; import java.util.concurrent.TimeUnit; import com.volshell.threads.UnsafeTask; public class UnsafeTaskTest { public static void main(String[] args) throws InterruptedException { UnsafeTask task = new UnsafeTask(); for (int i = 0; i < 10; i++) { Thread t = new Thread(task); t.start(); TimeUnit.SECONDS.sleep(2); } } }
按照一般思路理解每个线程结束的时间和开始的时间是不相同的,但是有些线程有相同的结束时间!!
下面是通过使用线程局部变量加以控制的,ThreadLocal
package com.volshell.threads; import java.util.Date; import java.util.concurrent.TimeUnit; public class SafeTask implements Runnable { private static ThreadLocal<Date> startDate = new ThreadLocal<Date>() { protected Date initialValue() { return new Date(); }; }; @Override public void run() { // TODO Auto-generated method stub System.out.println("Start thread :" + Thread.currentThread().getId() + ": " + startDate.get()); try { TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10)); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("----------Thread finished :" + Thread.currentThread().getId() + "--" + startDate.get()); } }
这样,每一个线程的起始、结束时间就完全不一样了。
二、深入源码
ThreadLocal类提供了thread-local variables ,通过set/get方法实现对变量的初始化。在第一次调用set的时候完成初始化。
get方法
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(); }
其实在其内部维护一个Map -- ThreadLocalMap,其内部同样适用Entry来实现,每一个线程用一个Entry存储。
static class Entry extends WeakReference<ThreadLocal> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal k, Object v) { super(k); value = v; } }
不过他的key为ThreadLocal对象。
回到get方法,通过当前线程获取ThreadLocalMap对象, ThreadLocalMap.Entry e = map.getEntry(this); 获取当前线程的Entry,然后就可以得到value.
set方法与之类似,
三、总结
每一个线程对应着一个ThreadLocalMap,Map中存储着Entry,而Entry中的key为ThreadLocal对象,value为真实的线程私有变量。