ThreadLocal与其他变量的区别

断了今生、忘了曾经 提交于 2020-04-07 16:28:36

今天学习了ThreadLocal,一直不知道它的用法,所以自己花时间写了个demo来理清楚ThreadLocal修饰的成员变量和类的普通成员的区别。

其实很简单,使用ThreadLocal<T>变量,一定要用public static来修饰,它的作用域是和public static修饰的普通变量一致的,但是普通变量是所有类共享的,而public static ThreadLocal<T>变量是每个线程自己私有的,并且可以在线程执行的过程中,随时获取,随时增删改,一直到线程执行完毕被销毁。

ThreadLocal是一个JAVA类,ThreadLocalMap是它的内部静态类。

ThreadLocalMap是一个定制的Map,它的key就是ThreadLocal本身,但是是弱引用的,value则是我们要保存的对象。

static class ThreadLocalMap {

    /**
     * The entries in this hash map extend WeakReference, using
     * its main ref field as the key (which is always a
     * ThreadLocal object).  Note that null keys (i.e. entry.get()
     * == null) mean that the key is no longer referenced, so the
     * entry can be expunged from table.  Such entries are referred to
     * as "stale entries" in the code that follows.
     */
    static class Entry extends WeakReference<ThreadLocal<?>> {
        /** The value associated with this ThreadLocal. */
        Object value;

        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }
}

线程类Thread,持有threadLocals 这个成员变量,表示每个线程,都有自己的map

ThreadLocal.ThreadLocalMap threadLocals = null;

下面我们来看一下DEMO:

package thread;

/**
 * @author yangjian
 * @date created in 13:32 2020/04/07
 */
public class TestThread extends Thread {


    @Override
    public void run() {
        Test test = new Test();
        TestThreadLocal testTL = new TestThreadLocal();
        test.process();
        test.testA();
        testTL.println();
    }

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

        TestThread a = new TestThread();
        TestThread b = new TestThread();
        TestThread c = new TestThread();
        TestThread d = new TestThread();
        TestThread e = new TestThread();


        a.start();
        b.start();
        c.start();
        d.start();
        e.start();
    }
}
package thread;

/**
 * @author yangjian
 * @date created in 13:52 2020/04/07
 */
public class Test {
    public static ThreadLocal<Long> local = new ThreadLocal<Long>() {
        protected Long initialValue() {
            System.out.println("initialValue threadId = " + Thread.currentThread().getId());
            return 0l;
        }
    };

    public Long A = 0l;

    public static Long B;

    public void process() {
        System.out.println("process threadId = " + Thread.currentThread().getId());
        Long result = local.get();
        result++;
        local.set(result);
        System.out.println("process=" + result);
    }

    public void testA() {
        System.out.println("testA threadId = " + Thread.currentThread().getId());
        A++;
        System.out.println("testA=" + A);
    }

}
package thread;

/**
 * @author yangjian
 * @date created in 15:25 2020/04/07
 */
public class TestThreadLocal {

    public void println() {
        System.out.println("println threadId = " + Thread.currentThread().getId());
        Test.local.set(Test.local.get() + 1);
        System.out.println("println=" + Test.local.get());
    }
}

输出结果:

process threadId = 15
process threadId = 14
process threadId = 13
process threadId = 11
initialValue threadId = 13
initialValue threadId = 14
process=1
initialValue threadId = 15
testA threadId = 13
process=1
initialValue threadId = 11
testA threadId = 14
testA=1
process=1
println threadId = 13
testA=1
process=1
println threadId = 14
println=2
testA threadId = 15
println=2
testA threadId = 11
testA=1
println threadId = 11
println=2
testA=1
println threadId = 15
println=2
process threadId = 12
initialValue threadId = 12
process=1
testA threadId = 12
testA=1
println threadId = 12
println=2

 

通过打印结果,我们发现不同线程的Test.local值,是不会互相影响的。

而且先在Test类中,执行了local.set()方法;然后再在TestThreadLocal类中,执行Test.local.get()方法,其结果是有前后影响的。

 

因为ThreadLocal使用了public static修饰,所以在不同的类中,都可以直接访问到它,这点和public static修饰的普通变量是一致的,说明它可以当全局变量在线程执行到不同的地方时,直接拿来使用。

但是普通的public static修饰的变量,在方法区中只保存一份,所有的线程共享它,对它进行操作,容易造成多线程并发问题,

而用public static ThreadLocal<T>修饰的变量,只针对每个线程私有,不会引发多线程并发问题。

 

为什么ThreadLocal要用public static来修饰?

如果不使用public static来修饰ThreadLocal变量,那它就跟普通成员变量没有区别了,会随着对象的创建而重新初始化,随着对象的销毁而销毁。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!