多線程應用中最關鍵的數據共享,當建立一個類實現了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都不一樣;從而保證了變量的線程安全;