ThreadLocal用法和实现原理

背景:

       如果你定义了一个单实例的java bean,它有若干属性,但是有一个属性不是线程安全的,比如说HashMap。

       并且碰巧你并不需要在不同的线程中共享这个属性,也就是说这个属性不存在跨线程的意义。

       那么你不要sychronize这么复杂的东西,ThreadLocal将是你不错的选择。

 why:

      因为线程同步限制了并发访问,会带来很大的性能损失。

误解:

      ThreadLocal很容易让人望文生义,想当然地认为是一个“本地线程”。

 正解:

       其实 ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易理解。

       从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。

 

举例来说:

package com.xzc.util;

import java.util.HashMap;

/**
 * @Descrption
 * @Author xingzc
 * @Date 2018/11/9 10:18
 */
public class TreadLocalTest {
    static ThreadLocal<HashMap> mapThreadLocal = new ThreadLocal<HashMap>() {
        @Override
        protected HashMap initialValue() {
            System.out.println(Thread.currentThread().getName() + "initialValue");
            return new HashMap();
        }
    };

    public void run() {
        Thread[] runs = new Thread[3];
        for (int i = 0; i < runs.length; i++) {
            runs[i] = new Thread(new T1(i));
        }
        for (int i = 0; i < runs.length; i++) {
            runs[i].start();
        }
    }

    public static class T1 implements Runnable {
        int id;

        public T1(int id0) {
            id = id0;
        }

        public void run() {
            System.out.println(Thread.currentThread().getName() + ":start");
            HashMap map = mapThreadLocal.get();
            for (int i = 0; i < 10; i++) {
                map.put(i, i + id * 100);
                try {
                    Thread.sleep(100);
                } catch (Exception ex) {
                }
            }
            System.out.println(Thread.currentThread().getName() + ':' + map);
        }
    }

    /**
     * Main
     *
     * @param args
     */
    public static void main(String[] args) {
        TreadLocalTest test = new TreadLocalTest();
        test.run();
    }

}

  

输出解释;

Thread-0:start
Thread-2:start
Thread-1:start
Thread-2initialValue
Thread-0initialValue
Thread-1initialValue
Thread-0:{0=0, 1=1, 2=2, 3=3, 4=4, 5=5, 6=6, 7=7, 8=8, 9=9}
Thread-2:{0=200, 1=201, 2=202, 3=203, 4=204, 5=205, 6=206, 7=207, 8=208, 9=209}
Thread-1:{0=100, 1=101, 2=102, 3=103, 4=104, 5=105, 6=106, 7=107, 8=108, 9=109}

  结论:

1) mapThreadLocal虽然是个静态变量,但是initialValue被调用了三次,通过debug发现,initialValue是从mapThreadLocal.get处发起的;

2) ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本 ,变量是线程安全的共享对象

3) ThreadLocal确实只有一个变量,但是它内部包含一个ThreadLocalMap,针对每个thread保留一个entry,如果对应的thread不存在则会调用initialValue;

 

源码解析:

      Theadlocal 代码片段;

/**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

 

/**
 * Get the map associated with a ThreadLocal. Overridden in
 * InheritableThreadLocal.
 *
 * @param  t the current thread
 * @return the map
 */
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

  

 

在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。 

 

参考

 @ ThreadLocal和线程同步机制对比

 

posted @ 2016-08-09 11:09  bcombetter  阅读(643)  评论(3)    收藏  举报