(一)线程管理_8---使用线程局部变量

使用线程局部变量

多线程应用中最关键的数据共享,当创建一个类实现了Runnable接口,该类中有一个私有属性;然后创建了多个都使用该类的线程,这些线程都共享这个属性,如果线程中有修改该属性的方法执行,那么该属性都将被这些线程修改,从而造成了错误;

有时候需要线程之间彼此互不相干,各自维护自己的局部变量;Java API提供了这样的机制,叫做 thread-local variable,即线程局部变量;

在下面的实例中展示这个机制,并简单的分析一下源码;

动手实现

(1)创建一个共享同一个属性的实例

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();
            }
        }
    }
}

一次运行结果

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

可以看到,线程启动的时间各部相同,因为创建每个线程之间睡眠了一秒钟;但是这些线程结束时间都是一样的;

(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();
            }
        }
    }
}

一次运行结果

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创建的,所以它们之间不会相干;

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都不同;从而保证了变量的线程安全;


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值