1.ThreadLocal用处
用来做线程封闭的
变量值的共享可以使用public static 变量值的形式,而如果想实现每一个线程都有自己的共享变量,就需要ThreadLocal类。他主要解决每个线程绑定自己的值,可以将ThreadLocal类比喻成全局存放数据的盒子,盒子中可以存放每个线程的私有数据。
类ThreadLocal解决的是变量在不同线程间的隔离性。也就是不用线程拥有自己的值,这些值可以放入ThreadLocal中进行保存。
2.多个线程数据的隔离性
package com.wx.threadlearn6;
public class Tools {
public static ThreadLocal t=new ThreadLocal();
}
线程A:
package com.wx.threadlearn6;
public class ThreadA extends Thread{
@Override
public void run() {
for (int i=0;i<100;i++)
{
Tools.t.set("ThreadA i="+(i));
System.out.println(Tools.t.get());
}
}
}
线程B:
package com.wx.threadlearn6;
public class ThreadB extends Thread {
@Override
public void run() {
for (int i=0;i<100;i++)
{
Tools.t.set("ThreadB i="+(i));
System.out.println(Tools.t.get());
}
}
}
测试:不同线程在ThreadLocal中拥有不同的值
package com.wx.threadlearn6;
public class Test1 {
public static void main(String[] args)
{
ThreadA threadA=new ThreadA();
ThreadB threadB=new ThreadB();
threadA.start();
threadB.start();
}
}
3.第一次调用ThreadLocal类是get()方法返回的是null,怎么样实现第一次调用返回有默认的值呢?
给ThradLocal赋初值:
package com.wx.threadlearn6;
import java.util.Date;
public class ThreadLocalExt extends ThreadLocal<Date> {
@Override
protected Date initialValue() {
return new Date();
}
}
测试:
package com.wx.threadlearn6;
public class Test2 {
public static void main(String[] args)
{
ThreadLocalExt localExt=new ThreadLocalExt();
System.out.println(localExt.get());
}
}
继续思考一个问题?多个线程会不会拥有同一个初值?
线程C:
package com.wx.threadlearn6;
public class ThreadC extends Thread {
private ThreadLocalExt threadLocalExt;
public ThreadC(ThreadLocalExt threadLocalExt)
{
this.threadLocalExt=threadLocalExt;
}
@Override
public void run() {
System.out.println("ThreadC中的初值"+threadLocalExt.get());
}
}
测试:
package com.wx.threadlearn6;
public class Test2 {
public static void main(String[] args)
{
try{
ThreadLocalExt localExt=new ThreadLocalExt();
System.out.println("Mian中的初值"+localExt.get());
Thread.sleep(1000);
ThreadC threadC=new ThreadC(localExt);
threadC.start();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
所以线程不同初值也不同。
4.如果想要在子线程中拿到父线程的值怎么做?即现在想要main线程中取值和ThreadC的取值一样该怎么办?
使用InheritableThreadLocal类,他可以让子线程从父线程中取值。
package com.wx.threadlearn6;
import java.util.Date;
public class InheritableThreadLocalExt extends InheritableThreadLocal {
@Override
protected Object initialValue() {
return new Date().getTime();
}
}
修改Tools:
package com.wx.threadlearn6;
public class Tools {
public static InheritableThreadLocalExt t=new InheritableThreadLocalExt();
}
package com.wx.threadlearn6;
public class ThreadC extends Thread {
@Override
public void run() {
System.out.println("ThreadC中的初值"+Tools.t.get());
}
}
测试:
package com.wx.threadlearn6;
public class Test2 {
public static void main(String[] args)
{
try{
System.out.println("Mian中的初值"+Tools.t.get());
Thread.sleep(1000);
ThreadC threadC=new ThreadC();
threadC.start();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
如果在继承时想要对值修改,重写childValue方法:继续上一个项目
package com.wx.threadlearn6;
import java.util.Date;
public class InheritableThreadLocalExt extends InheritableThreadLocal {
@Override
protected Object initialValue() {
return new Date().getTime();
}
@Override
protected Object childValue(Object parentValue) {
return parentValue+"子线程中加的";
}
}
5.ThreadLocal的原理:
1.ThreadLocal内部,维护了一个定制化的HashMap,其名名为ThreadLocalMap,可以看到这个ThreadLocalMap是从Thread类里面拿的,也就是说他这个本地变量是存放在具体的线程空间里面的。ThreadLocal本身不存数据。这个Map的key是ThreadLocal的this引用。
ThreadLocal的内存布局:因为存在弱引用ThreadLocal的实例被回收,内存中Entry的key就变成了NULL, 从而出现内存泄漏,解决的办法就是每次用完都要调用remove方法。
6.InheritableThreadLocal的使用
当我们在项目中可能经常会用到@Async注解来开启一个方法的异步执行,但是这样有个缺陷,就是在子线程中我们拿不到该次请求的主线程的用户信息。
所以我们这个时候可以利用InheritableThreadLocal存放用户的信息,然后在子线程中获取
主线程:
子线程:
直接在上下文塞入用户信息也是可以的