今天学习了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