(一)線程管理_8---使用線程局部變量

使用線程局部變量

多線程應用中最關鍵的數據共享,當建立一個類實現了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:53
Starting Thread_11 : 2014-11-01 20:42:54
Starting Thread_12 : 2014-11-01 20:42:55
Starting Thread_13 : 2014-11-01 20:42:56
Starting Thread_14 : 2014-11-01 20:42:57
Thread finished_10 : 2014-11-01 20:42:53
Thread finished_11 : 2014-11-01 20:42:54
Thread finished_14 : 2014-11-01 20:42:57
Thread finished_12 : 2014-11-01 20:42:55
Thread finished_13 : 2014-11-01 20:42:56

要點

ThreadLocal經過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;
}


threadLocals 上面註釋的是:ThreadLocal實現附屬到這個線程,而維護這些threadlocal的map是由ThreadLocal維護,即ThreadLocal的內部靜態類ThreadLocalMap;

在看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都不一樣;從而保證了變量的線程安全;
相關文章
相關標籤/搜索