页面加载中...

对ThreadLocal的理解

| Java | 0 条评论 | 780浏览

Preface

参考:http://www.cnblogs.com/dolphin0520/p/3920407.html

在Java并发编程中,最主要问题就是对共享的资源如何进行合理的调配。因为多个线程访问同一个资源时难免会有临界区的冲突,一般的解决方式都是进行合理的加锁。加锁的一个最大问题就是对程序的效率打折扣。但有时候,线程之间访问的同一个资源,这个资源在各个线程中是相互独立的,也就是说,A线程拥有的同B线程的资源,即使A线程对该资源被修改或其他操作,不影响B线程中该资源的值。在JDK中,就有这么一个类用于专门应用这种情况:ThreadLocal。

顾名思义,该类的字面意思就是 线程本地,网上其他也叫线程本地变量存储线程本地存储。 ThreadLocal可以针对某个资源变量为每个线程创建一个属于自己的副本变量。

ThreadLocal使用

ThreadLocal有四个方法:

public T get() { }
public void set(T value) { }
public void remove() { }
protected T initialValue() { }

get()即获取该副本变量;set()即设置副本变量;remove()即删除该副本变量;

initialValue()该方法交由子类去实现,设置一个初值,当没有set()值,使用get()方法就获取到该从初值

举个例子

在Holder类中有一个变量value,就作为线程要访问的资源,指定该资源的set及get方法。如下:

 static class Holder{
        //为每个线程在内部都会创建一个属于自己的变量副本
        private static final ThreadLocal threadLocalContext = new ThreadLocal();
                //作为资源
        String value;
        public void setValue(String value){
            threadLocalContext.set(value);
        }
        public String get(){
            return threadLocalContext.get();
        }
    }

定义线程的简单的操作,即设置下value,获取一下value。

class MyThread extends Thread{
        String value;
        public MyThread(String value){
            this.value = value;
        }
        @Override
        public void run() {
            holder.setValue(value);
            System.out.println("测试线程的值:"+holder.get());
        }
    }

主线程也是同样的操作,采用主线程与测试线程并发的操作,如下:

    @Test
    public void test01() throws InterruptedException {
        MyThread testThread = new MyThread("test");
        holder.setValue("main");
        System.out.println("main线程的值:"+holder.get());
        testThread.start();
        Thread.sleep(1000);
        System.out.println("main线程的值:"+holder.get());
    }

结果如下:

可见,一个线程对资源的改变并没有对另一个线程拥有的资源副本有影响。

一探究竟

ThreadLocalMap

进入ThreadLocal的set方法,源码如下:

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

源码很简单,就是根据当前线程的ThreadLocalMap,而后set进去。如果map为空,就先用createMap方法先创建一个map。createMap源码如下:

void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
 

发现创建一个Thread类中就有ThreadLocalMap这么一个变量,也就是说,每个线程都会维护ThreadLocalMap这么一个对象,既然是map,那么ThreadLocalMap就是存储着一些key、value的值。

 

ThreadLocalMap是ThreadLocal的一个内部类。其Entry的如下:

 

static class Entry extends WeakReference> {
        /** The value associated with this ThreadLocal. */
        Object value;
        Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
        }
}

从该Entry发现,该Entry是以ThreadLocal对象本身作为变量,以实际的Object值作为value。此外,该Entry是WeakRefrence(弱引用)的子类,并且key就是是弱引用。

先不说这个弱引用。从之前的Demo中就可以看出,我们往ThreadLoca中set一个值后,这个值其实最后就是保存到了当前线程的副本中(ThreadLocalMap中),以当前的ThreadLocal对象作为key。通过ThreadLocal的get方法,再从当前线程的副本中取出该值。

进而能够保证,存储同一个的Oject的对象,不同线程保存到各自内部后,对该值的操作是相互独立的。

发表评论

最新评论

    来第一个评论吧!