前言
在并发请求情况下,因为每次请求都有不同的用户信息,我们必须保证每次请求保存的用户信息互不干扰,线程独立。
注意:这里不是解决多线程资源共享问题,而是要保证每个线程都有自己的用户资源,互不干扰
ThreadLocal的作用主要是做数据隔离,填充的数据只属于当前线程,变量的数据对别的线程而言是相对隔离的,在多线程环境下,如何防止自己的变量被其它线程篡改。
ThreadLocal提供线程局部变量,一个变量用在多个线程中分别有独立的值(副本)
关键点:
- 每个线程(Thread)内部都持有一个ThreadLocalMap对象
- ThreadLocalMap的key是某个ThreadLocal对象,value是任意Object
- 不同线程,内部有自己的ThreadLocalMap,因此Map中的资源互相不会干扰
注意
- ThreadLocal将对象保存在线程中。换句话说就是每个线程的数据会相互隔离。基于这个特性我们可以将用户信息存储在这里,这样我们能保证我们的当前线程下执行分各种方法都能通过他获取到用户信息;
- ThreadLocal内部是将自己为key,存储对象为value存储到当前线程中的map中,这个map会随着线程的销毁而被JVM回收;
- 但是在我们实际开发中经常会使用线程池来避免线程的重复创建及销毁,那么线程往往是不会被销毁的;
- 在Spring中集成的类似Tomcat、JBoss等web容器中都是默认使用的一定数量的线程数的。而我们在Spring中使用的线程复用功能就导致了我们在获取当前线程的用户时因为此线程被别人使用过从未导致用户信息没有被更新成功,从而引发我们上面提到的奇怪的问题;
- 那么既然是没有被更新,到这里我们就很好解决了,要么每次使用完成后都将ThreadLocal中的数据remove,因为他内部是弱引用在下次回收就会将对象回收,这样也不会造成内存泄漏的问题。
基本使用
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserDTO {
private Long id;
private String nickName;
private String icon;
}
public class UserHolder {
private static final ThreadLocal<UserDTO> tl = new ThreadLocal<>();
public static void saveUser(UserDTO user){
tl.set(user);
}
public static UserDTO getUser(){
return tl.get();
}
public static void removeUser(){
tl.remove();
}
}
题外知识
- 什么是线程安全?
就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。
- 什么是线程不安全?
就是不提供数据访问保护机制,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。