深入学习ThreadLocal和InheritableThreadLocal

原创 2017年12月02日 22:20:19

最新看项目代码时, 发现有地方用到了InheritableThreadLocal,之前只用过ThreadLocal,于是就查了点资料,看了下源码,稍微学习一下。

本文地址:http://blog.csdn.net/v123411739/article/details/78697099

ThreadLocal

1.ThreadLocal的定义

这个类提供了线程局部变量。 这些变量与正常的变量不同,因为每个访问一个线程的线程(通过它的get或set方法)都有自己的,独立初始化的变量副本。 ThreadLocal实例通常是希望将状态与线程关联的类(例如,用户ID或事务ID)中的私有静态字段。只要线程处于活动状态并且ThreadLocal实例可以访问,每个线程就拥有对其线程局部变量副本的隐式引用; 在一个线程消失之后,线程本地实例的所有副本都会被垃圾收集(除非存在对这些副本的其他引用)。

2.ThreadLocalMap的定义

ThreadLocalMap是一个自定义哈希映射,仅适用于维护线程本地值。 没有任何操作被导出到ThreadLocal类之外。 该类是封装私有的,允许在Thread类中声明字段。 为了处理非常大和长期使用的用法,哈希表条目使用WeakReferences作为键。 但是,由于不使用引用队列,所以只有当表开始空间不足时才能删除旧条目。

ThreadLocalMap是ThreadLocal的静态内部类,是一个类似HashMap的实现,有兴趣的可以去看下源码,用法和HashMap没有区别,这里不在赘述。

3.ThreadLoca.set()方法

    /**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    /**
     * 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 values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;
先拿到当前线程,再使用getMap方法拿到当前线程的threadLocals变量,如果threadLocals不为空,则将当前ThreadLocal作为key,传入的值作为value,插入threadLocals;如果threadLocals为空则新建一个,然后将ThreadLocal作为key,传入的值作为value,插入threadLocals。注意此处的threadLocals变量是一个ThreadLocalMap,是Thread的一个局部变量,因此它只与当前线程绑定。

4.ThreadLoca.get()方法

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

    /**
     * Variant of set() to establish initialValue. Used instead
     * of set() in case user has overridden the set() method.
     *
     * @return the initial value
     */
    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;
    }
    protected T initialValue() {
        return null;
    }

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

跟set方法差不多,先拿到当前的线程,再使用getMap方法拿到当前线程的threadLocals变量,如果threadLocals不为空,则将当前ThreadLocal作为key,拿到对应的值;如果threadLocals为空,则会创建一个新的ThreadLocalMap,并将当前的ThreadLocal作为key,null作为value,插入到ThreadLocalMap,并返回null。注意上面的 initialValue()方法为protected,官方注释写到:如果程序员希望线程局部变量具有非null的初始值,则必须对ThreadLocal进行子类化,并重写此方法。 通常,将使用匿名内部类。

5.ThreadLocal.remove()方法

     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }
方法很简单,拿到当前线程的threadLocals属性,如果不为空,则将key为当前ThreadLocal的键值对移除。

6.功能测试

package com.chillax.test;

import java.util.concurrent.TimeUnit;

/**
 * ThreadLocal测试
 * 
 * @author JoonWhee
 * @Date 2017年12月2日
 */
public class TestThreadLocal {

    public static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();

    public static void main(String args[]) throws InterruptedException {

        Thread threadOne = new ThreadOne(); // 线程1
        Thread threadTo = new ThreadTo(); // 线程2
        threadTo.start(); // 线程2开始执行
        TimeUnit.MILLISECONDS.sleep(100); // 睡眠, 以等待线程2执行完毕
        threadOne.start(); // 线程1开始执行
        TimeUnit.MILLISECONDS.sleep(100); // 睡眠, 以等待线程1执行完毕
        // 此时线程1和线程2都已经设置过值, 此处输出为空, 说明子线程与父线程之间也是互不影响的
        System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get());

    }

    private static class ThreadOne extends Thread {
        @Override
        public void run() {
            // 此时线程2已经调用过set(456), 此处输出为空, 说明线程之间是互不影响的
            System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get());
            threadLocal.set(123);
            System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get());
        }
    }

    private static class ThreadTo extends Thread {
        @Override
        public void run() {
            threadLocal.set(456); // 设置当前ThreadLocal的值为456
            System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get());
        }
    }

}

输出结果:

Thread-1: 456
Thread-0: null
Thread-0: 123
main: null

总结:

1.每个线程都有一个ThreadLocalMap 类型的 threadLocals 属性。
2.ThreadLocalMap 类相当于一个Map,key 是 ThreadLocal 本身,value 就是我们的值。
3.当我们通过 threadLocal.set(new Integer(123)); ,我们就会在这个线程中的 threadLocals 属性中放入一个键值对,key 是 这个 threadLocal.set(new Integer(123))的threadlocal,value 就是值new Integer(123)。
4.当我们通过 threadlocal.get() 方法的时候,首先会根据这个线程得到这个线程的 threadLocals 属性,然后由于这个属性放的是键值对,我们就可以根据键 threadlocal 拿到值。 注意,这时候这个键 threadlocal 和 我们 set 方法的时候的那个键 threadlocal 是一样的,所以我们能够拿到相同的值。

5.ThreadLocalMap 的get/set/remove方法跟HashMap的内部实现都基本一样,都是通过hashCode和Entry长度-1进行位于运算得到我们想要找的索引位置,如果该索引位置的键值对不止1个,则遍历并找到我们所需的键值对然后进行相应操作即可,ThreadLocalMap 的hashCode跟HashMap不一样,有兴趣的可以去看看。

InheritableThreadLocal

1.InheritableThreadLocal的定义

InheritableThreadLocal继承了ThreadLocal,此类扩展了ThreadLocal以提供从父线程到子线程的值的继承:当创建子线程时,子线程接收父线程具有的所有可继承线程局部变量的初始值。 通常子线程的值与父线程的值是一致的。 但是,通过重写此类中的childValue方法,可以将子线程的值作为父线程的任意函数。

2.InheritableThreadLocal的源码

public class InheritableThreadLocal<T> extends ThreadLocal<T> {
    /**
     * Computes the child's initial value for this inheritable thread-local
     * variable as a function of the parent's value at the time the child
     * thread is created.  This method is called from within the parent
     * thread before the child is started.
     * <p>
     * This method merely returns its input argument, and should be overridden
     * if a different behavior is desired.
     *
     * @param parentValue the parent thread's value
     * @return the child thread's initial value
     */
    protected T childValue(T parentValue) {
        return parentValue;
    }

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

    /**
     * Create the map associated with a ThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the table.
     */
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}
InheritableThreadLocal的源码很短,只有3个很短的方法,我们主要关注getMap()方法和creatMap()方法,这两个方法都是重写的,跟ThreadLocal中的差别在于把ThreadLocal中的threadLocals换成了inheritableThreadLocals,这两个变量都是ThreadLocalMap类型,并且都是Thread类的属性。

    /* 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;

3.InheritableThreadLocal继承父线程的值

上文的定义说道了InheritableThreadLocal会继承父线程的值,这是InheritableThreadLocal被创造出来的意义,具体是怎么实现的?

让我们从子线程被创建出来开始看起

    public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null);
    }
    private void init(ThreadGroup g, Runnable target, String name,
            long stackSize, AccessControlContext acc) {
		// ... 省略掉一部分代码
		if (parent.inheritableThreadLocals != null)
		  this.inheritableThreadLocals =
		      ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
		// ... 省略掉一部分代码
	}
    static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
    }
        /**
         * Construct a new map including all Inheritable ThreadLocals
         * from given parent map. Called only by createInheritedMap.
         *
         * @param parentMap the map associated with parent thread.
         */
        private ThreadLocalMap(ThreadLocalMap parentMap) {
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];// 创建跟父线程相同大小的table

            for (int j = 0; j < len; j++) {// 遍历父线程的inheritableThreadLocals, 在上面第3个代码块作为参数传下来
                Entry e = parentTable[j];
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();// 拿到每个键值对的key, 即ThreadLocal对象
                    if (key != null) {
                        Object value = key.childValue(e.value);// 此处会调用InheritableThreadLocal重写的方法, 默认直接返回入参值
                        Entry c = new Entry(key, value);// 使用key和value构造一个Entry
                        int h = key.threadLocalHashCode & (len - 1);// 通过位与运算找到索引位置
                        while (table[h] != null)// 如果该索引位置已经被占,则寻找下一个索引位置
                            h = nextIndex(h, len);
                        table[h] = c;// 将Entry放在对应的位置
                        size++;
                    }
                }
            }
        }
这是线程被创建的整个流程,从第3个代码块我们可以知道当父线程的inheritableThreadLocals不为空时,当前线程的inheritableThreadLocals属性值会被直接创建,并被赋予跟父线程的inheritableThreadLocals属性一样的值,从最后一个代码块看出来(已在代码中做详细注释)。

此时我们知道了,当一个子线程创建的时候,会把父线程的inheritableThreadLocals属性的值继承到自己的inheritableThreadLocals属性。那么现在的问题是父线程的inheritableThreadLocals属性会有值吗?因为上文提到的ThreadLocal中,我们知道set()方法时,是把键值对放在threadLocals属性。这就要提到刚才说的InheritableThreadLocal重写的getMap()方法,因为InheritableThreadLocal类的set()和get()方法都是用的父类即ThreadLocal的方法,而从ThreadLocal的源码中我们知道,ThreadLocal的get()、set()、remove()方法都会先调用getMap()方法,而InheritableThreadLocal重写了该方法,因此此时返回的ThreadLocalMap为inheritableThreadLocals,所以我们知道了,当定义为InheritableThreadLocal时,操作的属性为inheritableThreadLocals而不是threadLocals。

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

4.功能测试

下面代码是对InheritableThreadLocal继承父线程的值的验证,可以看出,子线程确实拿到了父线程的值。



5.InheritableThreadLocal变量的可见性探讨

package com.chillax.test;

import java.util.concurrent.TimeUnit;

/**
 * InheritableThreadLocal可见性测试
 * 
 * @author JoonWhee
 * @author http://blog.csdn.net/v123411739
 * @Date 2017年12月2日
 */
public class TestInheritableThreadLocal2 {

    public static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();
    public static ThreadLocal<Integer> inheritableThreadLocal = new InheritableThreadLocal<Integer>();
    public static ThreadLocal<User> mutableInheritableThreadLocal = new InheritableThreadLocal<User>();
    public static ThreadLocal<User> mutableInheritableThreadLocalTo = new InheritableThreadLocal<User>();
    public static ThreadLocal<String> immutableInheritableThreadLocal = new InheritableThreadLocal<String>();
    public static ThreadLocal<String> immutableInheritableThreadLocalTo = new InheritableThreadLocal<String>();

    public static void main(String args[]) throws InterruptedException {
        // 测试0.ThreadLocal普通测试;
        // 结论0: ThreadLocal下子线程获取不到父线程的值
        threadLocal.set(new Integer(123)); // 父线程初始化

        Thread thread = new MyThread();
        thread.start();

        TimeUnit.MILLISECONDS.sleep(100); // 睡眠, 以等待子线程执行完毕
        System.out.println("main = " + threadLocal.get());
        System.out.println();

        // 测试1.InheritableThreadLocal普通测试;
        // 结论1: InheritableThreadLocal下子线程可以获取父线程的值
        inheritableThreadLocal.set(new Integer(123)); // 父线程初始化

        Thread threads = new MyThreadTo();
        threads.start();

        TimeUnit.MILLISECONDS.sleep(100); // 睡眠, 以等待子线程执行完毕
        System.out.println("main = " + inheritableThreadLocal.get());
        System.out.println();

        // 测试2.父线程和子线程的传递关系测试: 可变对象, 父线程初始化;
        // 结论2: 父线程初始化, Thread Construct浅拷贝, 共用索引, 子线程先get()对象, 再修改对象的属性,
        // 父线程跟着变, 注意: 此处子线程如果没有先get()直接使用set()一个新对象, 父线程是不会跟着变的
        mutableInheritableThreadLocal.set(new User("joon"));// 2.1父线程初始化

        Thread TestThread = new TestThread(); // 2.2先初始化父线程再创建子线程, 确保子线程能继承到父线程的User
        TestThread.start(); // 开始执行子进程

        TimeUnit.MILLISECONDS.sleep(100); // 睡眠, 以等待子线程执行完毕
        System.out.println("main = " + mutableInheritableThreadLocal.get()); // 2.5此处输出值为子线程修改的值, 因此可得出上述结论2
        System.out.println();

        // 测试3.父线程和子线程的传递关系测试: 可变对象, 父线程不初始化;
        // 结论3: 父线程没有初始化, 子线程初始化, 无Thread Construct浅拷贝, 子线程和主线程都是单独引用, 不同对象,
        // 子线程修改父线程不跟着变
        Thread TestThreadTo = new TestThreadTo(); // 3.1创建子进程
        TestThreadTo.start();

        TimeUnit.MILLISECONDS.sleep(100); // 睡眠, 以等待子线程执行完毕
        System.out.println("main = " + mutableInheritableThreadLocalTo.get()); // 3.3此处输出为null, 可得出上述结论3
        System.out.println();

        // 测试4.父线程和子线程的传递关系测试: 不可变对象, 父线程初始化;
        // 结论4: 父线程初始化, Thread Construct浅拷贝, 但由于不可变对象由于每次都是新对象, 所以互不影响
        immutableInheritableThreadLocal.set("joon");// 4.1父线程初始化

        Thread TestThreadTre = new TestThreadTre(); // 4.2先初始化父线程再创建子线程, 确保子线程能继承到父线程的值
        TestThreadTre.start(); // 执行子进程

        TimeUnit.MILLISECONDS.sleep(100); // 睡眠, 以等待子线程执行完毕
        System.out.println("main = " + immutableInheritableThreadLocal.get()); // 4.5此处输出为父线程的值, 因此可得出上述结论4
        System.out.println();

        // 测试5.父线程和子线程的传递关系测试: 不可变对象, 父线程不初始化;
        // 结论5: 父线程没有初始化, 子线程初始化, 无Thread Construct浅拷贝, 子线程和父线程操作不同对象, 互不影响
        Thread TestThreadFour = new TestThreadFour(); // 5.1创建子线程
        TestThreadFour.start();

        TimeUnit.MILLISECONDS.sleep(100); // 睡眠, 以等待子线程执行完毕
        System.out.println("main = " + immutableInheritableThreadLocalTo.get()); // 5.3此处输出为空, 因此可得出上述结论5
    }

    private static class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("MyThread = " + threadLocal.get());
        }
    }

    private static class MyThreadTo extends Thread {
        @Override
        public void run() {
            System.out.println("inheritableThreadLocal = " + inheritableThreadLocal.get());
        }
    }

    private static class TestThread extends Thread {
        @Override
        public void run() {
            // 2.3此处输出父线程的初始化对象值, 代表子线程确实继承了父线程的对象值
            System.out.println("TestThread.before = " + mutableInheritableThreadLocal.get());
            // 2.4子类拿到对象并修改
            mutableInheritableThreadLocal.get().setName("whee");
            System.out.println("mutableInheritableThreadLocal = " + mutableInheritableThreadLocal.get());
        }
    }

    private static class TestThreadTo extends Thread {
        @Override
        public void run() {
            mutableInheritableThreadLocalTo.set(new User("whee"));// 3.2子线程调用set方法
            System.out.println("mutableInheritableThreadLocalTo = " + mutableInheritableThreadLocalTo.get());
        }
    }

    private static class TestThreadTre extends Thread {
        @Override
        public void run() {
            // 4.3此处输出父线程初始化的值, 代表子线程确实继承了父线程的对象值
            System.out.println("TestThreadTre.before = " + immutableInheritableThreadLocal.get());
            // 4.4子线程调用set方法
            immutableInheritableThreadLocal.set("whee");
            System.out.println("immutableInheritableThreadLocal = " + immutableInheritableThreadLocal.get());
        }
    }

    private static class TestThreadFour extends Thread {
        @Override
        public void run() {
            immutableInheritableThreadLocalTo.set("whee");// 5.2子线程调用set方法
            System.out.println("immutableInheritableThreadLocalTo = " + immutableInheritableThreadLocalTo.get());
        }
    }

    private static class User {
        String name;

        public User(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "User [name=" + name + "]";
        }

    }
}

输出结果:



代码中都注释写清楚了,主要是根据存放可变对象 (User) 和不可变对象 (String)继续测试,根据输出结果可以得出以下结论:

1.对于可变对象:父线程初始化, 因为Thread Construct浅拷贝, 共用索引, 子线程修改父线程跟着变; 父线程不初始化, 子线程初始化, 无Thread Construct浅拷贝, 子线程和父线程都是单独引用, 不同对象, 子线程修改父线程不跟着变。

2.对于不可变对象:不可变对象由于每次都是新对象, 所以无论父线程初始化与否,子线程和父线程都互不影响。


从上面两条结论可知,子线程只能通过修改可变性(Mutable)对象对主线程才是可见的,即才能将修改传递给主线程,但这不是一种好的实践,不建议使用,为了保护线程的安全性,一般建议只传递不可变(Immuable)对象,即没有状态的对象。

虽然说不建议使用,但是有时候还是会碰到这种情况,如果想在修改子线程可变对象,同时不影响主线程,可以通过重写childValue()方法来实现。

6.重写childValue()方法实现子线程与父线程之间互不影响

package com.chillax.test;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 子线程与父线程实现完全互不影响
 * @author JoonWhee
 * @author http://blog.csdn.net/v123411739
 * @Date 2017年12月2日
 */
public class TestInheritableThreadLocal3 {

    private static final ThreadLocal<Map<Object, Object>> testThreadLocal = new InheritableThreadLocal<Map<Object, Object>>();
    private static final ThreadLocal<Map<Object, Object>> threadLocal = new InheritableThreadLocalMap<Map<Object, Object>>();

    public static void main(String args[]) throws InterruptedException {

        // 下面的测试1在上文已经做过(上文的测试2), 此处拿出来是为了进行比较
        // 测试1: 可变对象, 父线程初始化, 子线程先获取对象再修改对象值
        // 结论1: 子线程可以通过先获取对象再修改的方式影响父线程的对象值 
        Map<Object, Object> map = new HashMap<>();
        map.put("aa", 123);
        testThreadLocal.set(map);   // 父线程进行初始化
        
        Thread testThreadone = new TestThread();   // 创建子线程
        testThreadone.start();
        TimeUnit.MILLISECONDS.sleep(100);   // 父线程睡眠, 以等待子线程执行完毕 
        System.out.println("main = " + testThreadLocal.get());  // 此处输出为子线程的值, 说明子线程影响父线程的对象值
        System.out.println();
        
        // 测试2: 可变对象, 父线程初始化, 子线程先获取对象再修改对象值
        // 结论2: 通过重写childValue()实现子线程与父线程的互不影响
        Map<Object, Object> mapTo = new HashMap<>();
        mapTo.put("aa", 123);
        threadLocal.set(mapTo);   // 父线程进行初始化
        
        Thread testThread = new TestThreadTo();   // 创建子线程
        testThread.start();
        TimeUnit.MILLISECONDS.sleep(100);   // 父线程睡眠, 以等待子线程执行完毕 
        System.out.println("main = " + threadLocal.get());  // 此处输出为父线程的值, 说明子线程与父线程已经互不影响
        
    }
    
    private static class TestThread extends Thread {
        @Override
        public void run() {
            // 此处输出父线程的初始化对象值, 代表子线程确实继承了父线程的对象值
            System.out.println("TestThread.before = " + testThreadLocal.get());
            // 子类拿到对象并修改
            testThreadLocal.get().put("aa", 456);
            System.out.println("testThreadLocal = " + testThreadLocal.get());
        }
    }
    
    private static class TestThreadTo extends Thread {
        @Override
        public void run() {
            // 此处输出父线程的初始化对象值, 代表子线程确实继承了父线程的对象值
            System.out.println("TestThreadTo.before = " + threadLocal.get());
            // 子类拿到对象并修改
            threadLocal.get().put("aa", 456);
            System.out.println("threadLocal = " + threadLocal.get());
        }
    }

    private static final class InheritableThreadLocalMap<T extends Map<Object, Object>>
            extends InheritableThreadLocal<Map<Object, Object>> {
        // 重写ThreadLocal中的方法
        protected Map<Object, Object> initialValue() {
            return new HashMap<Object, Object>();
        }

        // 重写InheritableThreadLocal中的方法
        protected Map<Object, Object> childValue(Map<Object, Object> parentValue) {
            if (parentValue != null) {
                // 返回浅拷贝, 以达到使子线程无法影响主线程的目的
                return (Map<Object, Object>) ((HashMap<Object, Object>) parentValue).clone();
            } else {
                return null;
            }
        }
    }
}
输出结果

TestThread.before = {aa=123}
testThreadLocal = {aa=456}
main = {aa=456}

TestThreadTo.before = {aa=123}
threadLocal = {aa=456}
main = {aa=123}


通过结果,我们可以看出重写childValue()方法确实可以达到使子线程与主线程互不影响的效果。

版权声明:本文为博主原创文章,未经博主允许不得转载。博客地址:http://blog.csdn.net/v123411739

ThreadLocal与InheritableThreadLocal学习笔记

首先说明,现在我坚定的认为这两个东西并不是像有的博主说的是用来解决 “共享变量的访问冲突”的。那么他们是用来干嘛的呢?顾名思义他们都和当前线程的本地变量有关。1、ThreadLocala)先来看一段代...

InheritableThreadLocal用法与ThreadLocal的区别

public class TestThreadLocal { static final String VALUE01 = "test01"; static final String VALUE0...

ThreadLocal和InheritableThreadLocal的区别

1、ThreadLocal 主要解决每个线程绑定自己的值,达到线程直接隔离。 1.1如果第一次访问没有设定初始值,返回为null 1.2线程直接的隔离性//继承ThreadLocal类 publ...

InheritableThreadLocal-- 特殊的ThreadLocal

ThreadLocal允许我们在线程中保存一些变量,但是做不到父子线程数据的共享 public class InheritThreadLocal { public static Thre...

ThreadLocal与InheritableThreadLocal的使用

编写线程安全类是困难的。它不但要求仔细分析在什么条件可以对变量进行读写,而且要求仔细分析其它类能如何使用某个类。 有时,要在不影响类的功能、易用性或性能的情况下使类成为线程安全的是很困难的。有些类保留...

ThreadLocal&InheritableThreadLocal解惑

什么是ThreadLocal? 顾名思义它是local variable(线程局部变量)。它的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本...

ThreadLocal和InheritableThreadLocal的使用

ThreadLocal和InheritableThreadLocal的使用   ThreadLocal 与 InheritableThreadLocal 博客分类: java ...

Java多线程编程3--线程间通信--类ThreadLocal与类InheritableThreadLocal的使用

类ThreadLocal的使用     变量值的共享可以使用public stati。变量的形式,所有的线程都使用同一个publicstatic变量。如果想实现每一个线程都有自己的共享变量该如何解决...

Java 多线程学习笔记(十) InheritableThreadLocal的使用

废话少说,上代码package extthread; import tools.Tools; public class ThreadA extends Thread { @Override ...

JAVA ThreadLocal类深入

  • 2011年07月31日 10:20
  • 47KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深入学习ThreadLocal和InheritableThreadLocal
举报原因:
原因补充:

(最多只允许输入30个字)