目录
场景三:数据跨层传递(controller,service, dao)
场景四:Spring使用ThreadLocal解决线程安全问题
一、学习前言
多线程访问同一个共享变量时特别容易出现并发问题,特别是在多个线程需要对一个共享变量进行
写入时。
为了保证线程安全,一般使用者在访问共享变量时需要进行适当的同步,如图。
同步的措施一般是加锁,这就需要使用者对锁有一定的了解,这显然加重了使用者的负担。
那么有没有一种方式可以做到,当创建一个变量后,每个线程对其进行访问的时候,访问的是自己
线程的变量呢?
其实 ThreadLocal 就可以做这件事情,虽然 ThreadLocal 并不是为了解决这个问题而出现的。
ThreadLocal 是 JDK 包提供的,它提供了线程本地变量,也就是如果你创建了一个 ThreadLocal
变量,那么访问这个变量的每个线程都会有这个变量的一个本地副本。
当多个线程操作这个变量时,实际操作的是自己本地内存里面的变量,从而避免了线程安全问题。
创建一个 ThreadLocal 变量后,每个线程都会复制一个变量到自己的本地内存,如图所示。
二、认识ThreadLocal
ThreadLocal叫做线程变量,意思是ThreadLocal中填充的变量属于当前线程,该变量对其他线程而
言是隔离的,也就是说该变量是当前线程独有的变量。ThreadLocal为变量在每个线程中都创建了
一个副本,那么每个线程可以访问自己内部的副本变量。
ThreadLoal 变量,线程局部变量,同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不
同的副本。
这里有几点需要注意:
- 因为每个 Thread 内有自己的实例副本,且该副本只能由当前 Thread 使用。
这也是 ThreadLocal 命名的由来。
- 既然每个 Thread 有自己的实例副本,且其它 Thread 不可访问,那就不存在多线程间共享的问题。
ThreadLocal 提供了线程本地的实例。
它与普通变量的区别在于,每个使用该变量的线程都会初始化一个完全独立的实例副本。
ThreadLocal 变量通常被private static修饰。
当一个线程结束时,它所使用的所有 ThreadLocal 相对的实例副本都可被回收。
总的来说,ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,
也即变量在线程间隔离而在方法或类间共享的场景。
下图可以增强理解:
三、与Synchronized的区别
ThreadLocal其实就是与线程绑定的一个变量。
ThreadLocal和Synchonized都用于解决多线程并发访问。
但是ThreadLocal与synchronized有本质的区别:
- Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。
- Synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每
一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离
了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得
数据共享。
一句话理解ThreadLocal,threadlocl是作为当前线程中属性ThreadLocalMap集合中的某一个Entry
的key值Entry(threadlocl,value),虽然不同的线程之间threadlocal这个key值是一样,但是不同
的线程所拥有的ThreadLocalMap是独一无二的,也就是不同的线程间同一个ThreadLocal(key)
对应存储的值(value)不一样,从而到达了线程间变量隔离的目的,但是在同一个线程中这个 value
变量地址是一样的。
四、基本使用
直接上代码:
/**
* @projectName: 02JUC
* @ClassName ThreadLocaDemo01
* @description:
* @author: CodingW
* @create: 2025.01.17.15:14
* @Version 1.0
**/
public class ThreadLocaDemo01 {
private static ThreadLocal<String> localVar = new ThreadLocal<String>();