(一)线程管理_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都不一样;从而保证了变量的线程安全;
相关文章
相关标签/搜索