首页 > 其他分享 >JUC---多线程下的数据共享(基于ThreadLocal的思考)

JUC---多线程下的数据共享(基于ThreadLocal的思考)

时间:2024-11-16 13:15:35浏览次数:3  
标签:JUC inheritableThreadLocal 任务 数据共享 ThreadLocal 线程 TransmittableThreadLocal Inher

多线程下的数据共享(基于ThreadLocal的思考)

起初实在写项目过程中,在完成超时订单自动取消的任务时,使用xxl-job,整个逻辑是需要从订单表中找出过期的订单,然后将其存入订单取消表。

存入订单取消表时需要存储用户的信息。我最开始没想那么多,就直接从ThreadLocal中取出用户信息,但是后续的测试中发现用户信息为空,我就不断地调式不断地找问题。最后意识到定时任务运行在一个新的线程,不能从ThreadLocal中取出用户信息。其实那时候我知道如果线程切换就不能从ThreadLocal中取出用户信息这个问题,但是一直没往那方面去想,导致我调试了两个小时,最终意识到了这个问题。其实可以不从ThreadLocal中取用户信息的,因为从订单表查询订单的数据就含有用户的信息,然后我就利用订单中的用户信息进行后续操作了。

然后我就在想能不能有没有一种方式能使得定时任务这种新的线程可以访问ThreadLocal中的数据

有!!!但是和我项目中遇到的问题就没太大关系了

1.ThreadLocal

从字面含义上看,ThreadLocal即线程本地变量,可以依赖ThreadLocal保存线程私有的变量,主要实现思想是在每个线程中创建了⼀个副本,那么每个线程可以访问自己内部的副本变量。也就是说,同⼀个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本。ThreadLocal主要包含以下特点:

A:每个线程都维护自己的实例副本,且该副本只能由当前线程访问;
B:每个线程有自己的实例副本,且其它线程不可访问,所以也就不存在多线程间共享的问题

ThreadLocal并不支持继承性,那么如何做到在父子线程之间传递呢?

2.InheritableThreadLocal

ThreadLocal不同,InheritableThreadLocal允许子线程继承父线程(在这种情况下就是主线程)中设置的变量值。当在主线程中使用线程池创建子线程时,子线程可以自动获取到主线程在InheritableThreadLocal中设置的值。

总体上来说,InheritableThreadLocal实现跨线程数据同步主要分成三个操作:

1:继承ThreadLocal重写了Map的创建获取方法;
2:线程调用InheritableThreadLocal方法获取数据时,如果发现InheritableThreadLocal还没有初始化,那么会先执行初始化操作;
3:当线程创建新的线程时,线程初始化的时候会调用init方法将当前线程中的inheritableThreadLocal变量复制到新创建线程的inheritableThreadLocal中,从而实现跨线程数据共享。

class MyThreadLocalData {
	private static final InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();

	public static void setData(String data) {
		inheritableThreadLocal.set(data);
	}

	public static String getData() {
		return inheritableThreadLocal.get();
	}

	public class Main {
		public static void main(String[] args) {
			MyThreadLocalData.setData("Main thread data");
			ExecutorService executorService = Executors.newFixedThreadPool(3);
			for (int i = 0; i < 3; i++) {
				executorService.submit(() -> {
					System.out.println("Sub - thread got data: " + MyThreadLocalData.getData());
				});
			}
			executorService.shutdown();
		}
	}
}

虽然inheritableThreadLocal可以做到跨线程数据共享,但是仅限于父子线程,而且是在线程初始化的时候完成复制。而在实际开发过程中,大多数的业务场景都是使用线程池来避免线程频繁创建销毁的开销,此时inheritableThreadLocal起不到作用。

3.TransmittableThreadLocal

TransmittableThreadLocal(TTL)是阿里巴巴开源的一个用于解决在多线程环境下,线程局部变量跨线程传递问题的工具类,它是对 Java 标准库中ThreadLocal的增强和扩展

  • TransmittableThreadLocal(TTL)是一个用于解决在多线程环境下,特别是在使用线程池时ThreadLocal值传递问题的工具类。
  • 当使用普通的ThreadLocal时,在线程池环境下,由于线程可能被复用,会导致ThreadLocal的值在不同任务之间产生混淆。而TransmittableThreadLocal能够确保在任务提交到线程池时,正确地传递ThreadLocal的值,并且在任务执行完后恢复线程的原始状态。

工作原理

  • 包装任务
    • 当向线程池提交任务时,TransmittableThreadLocal对任务进行包装。例如,在 Java 中,如果使用ExecutorService提交一个Runnable任务,TransmittableThreadLocal会将这个Runnable包装成一个新的Runnable,这个新的Runnable会在执行前获取当前线程(提交任务的线程)中的TransmittableThreadLocal值,并将这些值传递到执行任务的线程中。
  • 值的传递与恢复
    • 在任务执行过程中,执行任务的线程可以像使用普通ThreadLocal一样使用TransmittableThreadLocal,获取到从提交任务的线程传递过来的值。当任务执行完毕后,TransmittableThreadLocal会将执行任务的线程的状态恢复到执行任务之前的状态,避免对线程池中的线程状态造成持久的影响。
public class TTLExample {
	private static final TransmittableThreadLocal<String> ttl = new TransmittableThreadLocal<>();

	public static void main(String[] args) {
		ttl.set("Main thread value");
		ExecutorService executorService = Executors.newFixedThreadPool(2);
		executorService.submit(() -> {
			System.out.println("Sub - thread got value: " + ttl.get());
		});
		ttl.remove();
		executorService.shutdown();
	}
}

标签:JUC,inheritableThreadLocal,任务,数据共享,ThreadLocal,线程,TransmittableThreadLocal,Inher
From: https://blog.csdn.net/weixin_73205368/article/details/143815947

相关文章

  • 跨企业、跨区域的 FMEA 数据共享和协作模式的探索
    【大家好,我是唐Sun,唐Sun的唐,唐Sun的Sun。】在当今全球化和高度互联的商业环境中,跨企业、跨区域的FMEA(失效模式及后果分析)数据共享和协作模式具有重要意义。首先,建立统一的数据标准和格式是实现有效共享的基础。不同企业和区域可能采用各自独特的FMEA记录方式和术语,这会导......
  • 多线程的入门与理解
    文章目录一、认识多线程1.1概念(1)线程是什么(2)为什么要有线程(3)进程和线程的区别(4)java的线程与操作系统线程的关系1.2第一个多线程程序1.3创建线程方法一继承Thread类方法二实现Runnable接口其他变形1.4多线程的优势二、Thread类及常见方法2.1Thread的常见构造方法2.2......
  • 多线程篇·线程相关知识
    一、线程状态线程是cpu任务调度的最小执行单位,每个线程拥有自己独立的程序计数器、虚拟机栈、本地方法栈。线程状态包括:创建、就绪、运行、阻塞、死亡。二、线程状态切换三、阻塞唤醒过程阻塞以下三个方法的调用都会使当前线程阻塞,该线程将会被放置到对该Object的请......
  • 多线程篇·线程相关知识
    一、线程状态线程是cpu任务调度的最小执行单位,每个线程拥有自己独立的程序计数器、虚拟机栈、本地方法栈。线程状态包括:创建、就绪、运行、阻塞、死亡。二、线程状态切换三、阻塞唤醒过程阻塞以下三个方法的调用都会使当前线程阻塞,该线程将会被放置到对该Object的请......
  • 实战指南:理解 ThreadLocal 原理并用于Java 多线程上下文管理
    目录一、ThreadLocal基本知识回顾分析(一)ThreadLocal原理(二)既然ThreadLocalMap的key是弱引用,GC之后key是否为null?(三)ThreadLocal中的内存泄漏问题及JDK处理方法(四)部分核心源码回顾ThreadLocal.set()方法源码详解ThreadLocalMap.get()方法详解ThreadLocal.remove()方法源......
  • python多线程和网络编程
    一、多线程1.进程、线程和并行执行学习目标:了解什么是进程、线程,了解什么是并行执行进程比作公司,线程比作员工,多线程并行执行就比作公司的不同员工在同一时间去做不同的事。总结2.多线程编程学习目标:掌握使用threading模块完成多线程编程当你想实现唱歌和跳舞一......
  • 【JavaEE初阶 — 多线程】生产消费模型 & 阻塞队列
         1.阻塞队列     (1)阻塞队列   1.概念   阻塞队列是一种特殊的队列,也遵守"先进先出"的原则;阻塞队列能是一种线程安全的数据结构,主要用来阻塞队列的插入和获取操作:当队列满了的时候,插入操作会被阻塞,直到队列有空位。当队列为空的时......
  • Java面试之多线程&并发篇(3)
    前言本来想着给自己放松一下,刷刷博客,突然被几道面试题难倒!SynchronizedMap和ConcurrentHashMap有什么区别?什么是线程安全?Thread类中的yield方法有什么作用?Java线程池中submit()和execute()方法有什么区别?似乎有点模糊了,那就大概看一下面试题吧。好记性不如烂键盘***12......
  • Java面试之多线程&并发篇(3)
    前言本来想着给自己放松一下,刷刷博客,突然被几道面试题难倒!SynchronizedMap和ConcurrentHashMap有什么区别?什么是线程安全?Thread类中的yield方法有什么作用?Java线程池中submit()和execute()方法有什么区别?似乎有点模糊了,那就大概看一下面试题吧。好记性不如烂键盘***12万字的j......
  • python——多线程、多进程接口测试multiprocessing
    一、线程、进程的区别https://www.zhihu.com/question/25532384二、实际使用场景登录3个用户,然后用这3个用户的session,进行10个进程,或者10个线程的操作(因为登录后没有退出登录,也没有被其他地方登录挤掉账号,所以session在一段时间内有效。且当前系统允许使用同一个session进行多......