ThreadLocal为变量在每个线程中都创建了一个副本,一个ThreadLocal在一个线程中是共享的,在不同线程之间又是隔离的,也就说说每个线程都只能看到自己线程的值。
实现线程范围的局部变量
initialValue():返回此线程局部变量的当前线程的初始值,默认为null
get():返回当前线程的此线程局部变量的副本中的值
set(T value):将当前线程的此线程局部变量的副本设器为指定的值。
remove():删除此线程局部变量的当前线程的值。
withInitial(Supplier extends S> supplier):创建线程局部变量。
package com.tangbb.test1;/*** @DESCRIPTION:* @USER: tangbingbing* @DATE: 2023/3/13 9:49*/
public class test1 {private static ThreadLocal num = new ThreadLocal() {// 重写这个方法,可以修改“线程变量”的初始值,默认是null@Overrideprotected Integer initialValue() {return 0;}};public static void main(String[] args) {// 创建一号线程new Thread(new Runnable() {@Overridepublic void run() {// 在一号线程中将ThreadLocal变量设置为1num.set(1);System.out.println("一号线程中ThreadLocal变量中保存的值为:" + num.get());}}).start();// 创建二号线程new Thread(new Runnable() {@Overridepublic void run() {num.set(2);System.out.println("二号线程中ThreadLocal变量中保存的值为:" + num.get());}}).start();//为了让一二号线程执行完毕,让主线程睡500mstry {Thread.sleep(500);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println("主线程中ThreadLocal变量中保存的值:" + num.get());}
}
在类中创建一个静态的ThreadLocal变量,在主线程中创建两个线程,在这两个线程中分别设置ThreadLocal变量为1和2。然后等待一号和二号线程执行完毕后,在主线程中查看ThreadLocal变量的值
每个Thread对象都有一个ThreadLocalMap,当创建一个ThreadLocal的时候,就会将该ThreadLocal对象添加到该Map中,其中键就是ThreadLocal,值可以是任意类型。
ThreadLocal 提供了线程本地的实例。它与普通变量的区别在于,每个使用该变量的线程都会初始化一个完全独立的实例副本。ThreadLocal 变量通常被private static修饰。当一个线程结束时,它所使用的所有 ThreadLocal 相对的实例副本都可被回收。
为了防止OOM问题ThreadLocal在使用的时候要及时remove
package com.tangbb.test1;/*** @DESCRIPTION:* @USER: tangbingbing* @DATE: 2023/3/13 9:49*/
public class test1 {private static ThreadLocal localVar = new ThreadLocal();static void print(String str) {//打印当前线程中本地内存中本地变量的值System.out.println(str + " :" + localVar.get());//清除本地内存中的本地变量localVar.remove();}public static void main(String[] args) throws InterruptedException {new Thread(new Runnable() {public void run() {test1.localVar.set("local_A");print("A");//打印本地变量System.out.println("after remove : " + localVar.get());}},"A").start();Thread.sleep(1000);new Thread(new Runnable() {public void run() {test1.localVar.set("local_B");print("B");System.out.println("after remove : " + localVar.get());}},"B").start();}}
ThreadLocal可以解决具有以下两个需求的场景:
1.每个线程需要有自己单独的实例
2.实例需要在多个方法中共享,但不希望被多线程共享
数据跨层传递(controller,service, dao)
比如说我们是一个用户系统,那么当一个请求进来的时候,一个线程会负责执行这个请求,然后这个请求就会依次调用service-1()、service-2()、service-3()、service-4(),这4个方法可能是分布在不同的类中的。
package com.kong.threadlocal;public class ThreadLocalDemo05 {public static void main(String[] args) {User user = new User("jack");new Service1().service1(user);}}class Service1 {public void service1(User user){//给ThreadLocal赋值,后续的服务直接通过ThreadLocal获取就行了。UserContextHolder.holder.set(user);new Service2().service2();}
}class Service2 {public void service2(){User user = UserContextHolder.holder.get();System.out.println("service2拿到的用户:"+user.name);new Service3().service3();}
}class Service3 {public void service3(){User user = UserContextHolder.holder.get();System.out.println("service3拿到的用户:"+user.name);//在整个流程执行完毕后,一定要执行removeUserContextHolder.holder.remove();}
}class UserContextHolder {//创建ThreadLocal保存User对象public static ThreadLocal holder = new ThreadLocal<>();
}class User {String name;public User(String name){this.name = name;}
}执行的结果:service2拿到的用户:jack
service3拿到的用户:jack
数据库连接,处理数据库事务