今天学习了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变量,那它就跟普通成员变量没有区别了,会随着对象的创建而重新初始化,随着对象的销毁而销毁。
来源:oschina
链接:https://my.oschina.net/xiaoyoung/blog/3223240