多线程应用中最关键的数据共享,当建立一个类实现了Runnable接口,该类中有一个私有属性;而后建立了多个都使用该类的线程,这些线程都共享这个属性,若是线程中有修改该属性的方法执行,那么该属性都将被这些线程修改,从而形成了错误;java
有时候须要线程之间彼此互不相干,各自维护本身的局部变量;Java API提供了这样的机制,叫作 thread-local variable,即线程局部变量;安全
在下面的实例中展现这个机制,并简单的分析一下源码;多线程
(1)建立一个共享同一个属性的实例dom
public class UnsafeTask implements Runnable { // Unsafe variable private Date startDate; @Override public void run() { startDate = new Date(); System.out.printf("Starting Thread_%s : %s\n", Thread.currentThread().getId(), Utils.dateFormat(startDate)); try { TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10 + 5)); } catch (InterruptedException e) { e.printStackTrace(); } System.out.printf("Thread finished_%s : %s\n", Thread.currentThread().getId(), Utils.dateFormat(startDate)); } public static void main(String[] args) { UnsafeTask unsafeTask = new UnsafeTask(); for (int i = 0; i < 5; i++) { Thread thread = new Thread(unsafeTask); thread.start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } } }
一次运行结果ide
Starting Thread_10 : 2014-11-01 20:40:24
Starting Thread_11 : 2014-11-01 20:40:25
Starting Thread_12 : 2014-11-01 20:40:26
Starting Thread_13 : 2014-11-01 20:40:27
Starting Thread_14 : 2014-11-01 20:40:28
Thread finished_11 : 2014-11-01 20:40:28
Thread finished_13 : 2014-11-01 20:40:28
Thread finished_12 : 2014-11-01 20:40:28
Thread finished_10 : 2014-11-01 20:40:28
Thread finished_14 : 2014-11-01 20:40:28
源码分析
能够看到,线程启动的时间各部相同,由于建立每一个线程之间睡眠了一秒钟;可是这些线程结束时间都是同样的;this
(2)建立一个线程局部变量的实例线程
public class SafeTask implements Runnable { private static ThreadLocal<Date> startDate=new ThreadLocal<Date>(){ @Override protected Date initialValue() { return new Date(); } }; @Override public void run() { System.out.printf("Starting Thread_%s : %s\n", Thread.currentThread().getId(), Utils.dateFormat(startDate.get())); try { TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10+5)); } catch (InterruptedException e) { e.printStackTrace(); } System.out.printf("Thread finished_%s : %s\n", Thread.currentThread().getId(), Utils.dateFormat(startDate.get())); } public static void main(String[] args) { SafeTask safeTask = new SafeTask(); for (int i = 0; i < 5; i++) { Thread thread = new Thread(safeTask); thread.start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } } }
一次运行结果code
Starting Thread_10 : 2014-11-01 20:42:53ThreadLocal经过initValue()方法建立并维护线程的这个属性值,能够经过get方法获取到;每个启动的线程都将有一个ThreadLocal实例;这些实例都是经过new建立的,因此它们之间不会相干;orm
ThreadLocal提供了remove()方法,线程能够经过该方法删除其threadLocal中的变量;
首先看下Thread类中的几个属性
public class Thread implements Runnable { private Runnable target; /* 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; }
在看ThreadLocal类的几个方法
public class ThreadLocal<T> { 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(); } ThreadLocalMap getMap(Thread t) { return t.threadLocals; } private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; } void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); } }能够看出,Thread类中的threadlocalMap是经过ThreadLocal中的set方法来引用和维护的;在一个线程启动后,当在线程内获取threadlocal局部变量时,threadlocal经过set方法将自身的引用设置到当前线程的ThreadLocalMap中,并经过initValue方法返回新建立的值,每一个线程维护的threadlocal都不一样;从而保证了变量的线程安全;