文章摘要
GPT 4
此内容根据文章生成,仅用于文章内容的解释与总结
投诉

什么是线程安全

这篇博客内容很好,直接偷过来:

什么是线程安全?如何保证线程安全?-CSDN博客

如何保证线程安全

第一种:加锁。

在 Java 中通过添加synchronized关键字实现,对方法或者代码块进行互斥。

第二种:CAS非阻塞同步。

通过不断自旋进行重试,避免线程进入阻塞状态,挂起和唤醒都需要性能开销。

前面两种方式可以参考上面的博客和我写的锁的内容:Java多线程:(三)多线程锁、JUC锁的实现

第三种:ThreadLocal无同步方案。

线程本地存储:将共享数据的可见范围限制在一个线程中。这样无需同步也能保证线程之间不出现数据争用问题。

这种方式我结合后端开发一起介绍。

SpringBoot + ThreadLocal

通常,我们会使用 synchronzed 关键字 或者 lock锁 来控制线程对临界区资源的同步顺序,但这种加锁的方式会让未获取到锁的线程进行阻塞,很显然,这种方式的时间效率不会特别高。

线程安全问题的核心在于多个线程会对同一个临界区的共享资源进行访问,那如果每个线程都拥有自己的“共享资源”,各用各的,互不影响,这样就不会出现线程安全的问题了,对吧?

顾名思义,ThreadLocal 就是线程的“本地变量”,即每个线程都拥有该变量的一个副本,达到人手一份的目的,这样就可以避免共享资源的竞争

ThreadLocal源码分析

理清ThreadLocal、ThreadLocalMap、Thread之间的关系 - 翎野君 - 博客园 (cnblogs.com)

ThreadLocal工具类 + springboot

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
ThreadLocal工具类代码
package com.example.tadakai.utils;

/**
* ThreadLocal 工具类
*/
@SuppressWarnings("all")
public class ThreadLocalUtil {
//提供ThreadLocal对象,
private static final ThreadLocal THREAD_LOCAL = new ThreadLocal();

//根据键获取值
public static <T> T get(){
return (T) THREAD_LOCAL.get();
}

//存储键值对
public static void set(Object value){
THREAD_LOCAL.set(value);
}
//清除ThreadLocal 防止内存泄漏
public static void remove(){
THREAD_LOCAL.remove();
}
}

当每一个用户访问程序的时候,都会为一个用户开辟一个独立的进程,在拦截器的代码中,可以用ThreadLocal的实现类对象调用set方法记录id,在之后的三层架构中,再使用get方法获取到这个线程中存储过的id,以达到减少传递参数的目的

img

使用ThreadLocal后,可以确保访问的数据是同一个用户,所以不需要核验请求头(使用@RequestHeader参数获取请求头)

imgimg

使用完ThreadLocal后,避免资源泄露,需要在拦截器响应结束后释放资源,即重写拦截器的aftercompletion方法,方法中调用ThreadLocal的remove方法

img