精华内容
下载资源
问答
  • SpringBoot 异步线程传递上下文

    千次阅读 2020-12-11 09:59:04
    SpringBoot异步线程传递上下文需求实现启用异步功能配置异步配置任务装饰器完工 需求 SpringBoot项目中,经常使用@Async来开启一个子线程来完成异步操作。主线程中的用户信息需要传递给子线程 实现 启用异步功能 ...

    需求

    SpringBoot项目中,经常使用@Async来开启一个子线程来完成异步操作。主线程中的用户信息需要传递给子线程

    实现

    启用异步功能

    在启动类里加上@EnableAsync注解

    @EnableAsync
    @SpringBootApplication
    public class Application {}
    

    配置异步

    新建一个配置类,实现AsyncConfigurer接口,并重写getAsyncExecutor方法

    @Configuration
    public class AsyncConfig implements AsyncConfigurer {
    
        @Override
        public Executor getAsyncExecutor() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(10);
            executor.setMaxPoolSize(50);
            executor.setThreadNamePrefix("async-pool-");
            // 这一步是关键,异步Task装饰器
            executor.setTaskDecorator(new MyContextDecorator());
            executor.initialize();
            return executor;
        }
    }
    

    配置任务装饰器

    新建一个异步任务装饰器,实现TaskDecorator接口,并重写decorate方法

    public class MyContextDecorator implements TaskDecorator {
        @Override
        @Nonnull
        public Runnable decorate(@Nonnull Runnable runnable) {
    
    		// 获取主线程中的请求信息(我们的用户信息也放在里面)
           RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
            return () -> {
                try {
                  	// 将主线程的请求信息,设置到子线程中
                  	RequestContextHolder.setRequestAttributes(attributes);
                 	// 执行子线程,这一步不要忘了
                    runnable.run();
                } finally {
                	// 线程结束,清空这些信息,否则可能造成内存泄漏
                    RequestContextHolder.resetRequestAttributes();
                }
            };
        }
    

    补充:RequestContextHolder内部是基于ThreadLocal实现的,因此在使用set get时,都是和当前线程绑定的。当然,使用者的用户信息不一定放在了RequestContextHolder里面,读者可以自行扩展。

    完工

    到此,通过@Async开启的子线程,就可以正常拿到父线程中的Request信息了。

    展开全文
  • 后续文章首发在个人博客,欢迎移驾我的个人博客浏览该文章,地址在下方 多线程篇-父子线程上下文传递

    后续文章首发在个人博客,欢迎移驾我的个人博客浏览该文章,地址在下方

    多线程篇-父子线程的上下文传递

    展开全文
  • 从朋友那里借鉴来的思路,他当时遇到的业务情况是这样的: 每个用户登陆系统后,该用户需要异步执行多个方法,方法内涉及到...aop拦截子线程的调用,将上下文通过封装的Runnable对象传递给子线程。因为aop是beanPos...

    该项目已合并到 git项目microservice-sc-v1.0 dev分支中,具体位置为microservice-sc-v1.0/module-service-hi/src/main/java/servicehi/asyncthreadaop/,假如对您的需求有帮助,可不可以不吝给颗star,您的肯定是我的动力。

    从朋友那里借鉴来的思路,他当时遇到的业务情况是这样的:

    每个用户登陆系统后,该用户需要异步执行多个方法,方法内涉及到从securityContext和LogContext中读取用户信息
    

    抽象出来便是

    用户登录的主线程需要异步(多子线程)执行多个方法,方法中需要保持用户的上下文信息
    

    解决思路:

    aop拦截子线程的调用,将上下文通过封装的Runnable对象传递给子线程。因为aop是beanPostProcessor后处理器,只能拦截1)bean中的2)方法,所以逆推得 子线程来自 一个2)线程池的submit执行合适一些,而且这个线程池对象是1)一个bean。
    

    实现:

    1、bean形式存在的线程池

    @Configuration
    public class AsynExecutor {
    	
    	@Bean
    	@Lazy
    	public ExecutorService defaultExecutor() {
    		return Executors.newCachedThreadPool();
    	}
    
    }
    

    2、该线程池提供的线程可以通过注解的方式被业务方法体异步使用

    @Async("defaultExecutor")
    @Target({ElementType.METHOD,ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface DefaultAsyncThread {
    
    	
    }
    

    3、标注如上注解的方法在被线程池中线程执行时添加AOP

    public class DefaultExecutorSubmitAspect {
    
    	@Around("execution(* java.util.concurrent.Executor.*(..))")
    	@SneakyThrows
    	public Object intercept(ProceedingJoinPoint joinPoint) {
    		
    		Object[] args = joinPoint.getArgs();
    		for(int i =0; i< args.length; i++) {
    			args[i] = processArgs(args[i]);
    		}
    		
    		return joinPoint.proceed();
    	}
    	
    	Object processArgs(Object arg) {
    		if(arg instanceof Runnable) {
    			Runnable r = (Runnable)arg;
    			return new RunnableWeave(r);
    		}
    		if(arg instanceof Callable<?>) {
    			Callable<?> c = (Callable<?>)arg;
    			return new CallableWeave<>(c);
    		}
    		if(arg instanceof Collection<?>) {
    			Collection<?> cs = (Collection<?>)arg;
    			List<Object> collect = cs.stream().map(this::processArgs).collect(Collectors.toList());
    			return collect;
    		}
    		return arg;
    	}
    }
    

    4、将上下文传递给子线程,从而保证 子线程里可以读取用户登录信息(父线程上下文信息)

    public class RunnableWeave  implements Runnable {
    
    	private final Runnable r;
    	private final Map<String, String> MDCContextMap;
    	private final SecurityContext securityContext;
    	
    	public RunnableWeave(Runnable r) {
    		this.r = r;
    		this.MDCContextMap = MDC.getCopyOfContextMap();
    		securityContext = SecurityContextHolder.getContext();
    	}
    	
    	@Override
    	public void run() {
    		MDC.setContextMap(MDCContextMap);
    		SecurityContextHolder.setContext(securityContext);
    		r.run();
    		SecurityContextHolder.clearContext();
    		MDC.setContextMap(new HashMap<>());
    	}
    
    }
    
    public class CallableWeave<V> implements Callable<V> {
    
    	private final Callable<V> c;
    	private final Map<String, String> MDCContextMap;
    	private final SecurityContext securityContext;
    	
    	public CallableWeave(Callable<V> c) {
    		this.c = c;
    		this.MDCContextMap = MDC.getCopyOfContextMap();
    		this.securityContext = SecurityContextHolder.getContext();
    	}
    	@Override
    	public V call() throws Exception {
    		MDC.setContextMap(MDCContextMap);
    		SecurityContextHolder.setContext(securityContext);
    		V v = c.call();
    		MDC.setContextMap(new HashMap<>());
    		SecurityContextHolder.clearContext();
    		return v;
    	}
    
    }
    
    展开全文
  • 看这篇记一次线上采坑实录)会导致上下文失效。 但是spring 4.3给出了好的方案,利用TaskDecorator。 看这个名称大概就能猜出是一个装饰器设计原理 我们分析下线程池的源码 @Override protected ...

    一般同步编程模型中我们使用ThreadLocal即可,但是在异步编程模型中(可能有同学有疑问,为什么不用InheritThreadLocal?看这篇记一次线上采坑实录)会导致上下文失效。

    但是spring 4.3给出了好的方案,利用TaskDecorator。

    看这个名称大概就能猜出是一个装饰器设计原理

    我们分析下线程池的源码

    @Override
    	protected ExecutorService initializeExecutor(
    			ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
    
    		BlockingQueue<Runnable> queue = createQueue(this.queueCapacity);
    
    		ThreadPoolExecutor executor;
            //当线程池的装饰器不为空时,执行execute方法会进入这里,因为它重写了execute方法
    		if (this.taskDecorator != null) {
    			executor = new ThreadPoolExecutor(
    					this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,
    					queue, threadFactory, rejectedExecutionHandler) {
                    
                    //这里是一个代理设计模式的实现,对execute做了一层代理
    				@Override
    				public void execute(Runnable command) {
                        //执行装饰器的逻辑,注意这段代码是在主线程中运行
    					Runnable decorated = taskDecorator.decorate(command);
    					if (decorated != command) {
    						decoratedTaskMap.put(decorated, command);
    					}
                        //子线程真正执行的方法(异步模块)...初始化核心线程数,核心线程满了入队列,队列满开启至最大线程数
    					super.execute(decorated);
    				}
    			};
    		}
    		else {
    			executor = new ThreadPoolExecutor(
    					this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,
    					queue, threadFactory, rejectedExecutionHandler);
    
    		}
    
    		if (this.allowCoreThreadTimeOut) {
    			executor.allowCoreThreadTimeOut(true);
    		}
    
    		this.threadPoolExecutor = executor;
    		return executor;
    	}

    demo演示

    主线程16个,子线程2个,执行10次,目的是尽可能让子线程复用。

    @Test
        public void testThreadLocal() {
            ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
            ExecutorService mainThreadPool = Executors.newFixedThreadPool(16);
    
            ThreadPoolTaskExecutor childThreadPool = new ThreadPoolTaskExecutor();
            childThreadPool.setCorePoolSize(2);
            childThreadPool.setMaxPoolSize(2);
    
            childThreadPool.setTaskDecorator(runnable -> {
                int v = threadLocal.get();
                System.out.println("装饰器中获取到主线程=" + Thread.currentThread().getName() + " 获取上下文=" + v);
                return () -> {
                    try {
                        //重新copy传递给子线程
                        threadLocal.set(v);
                        runnable.run();
                    } finally {
                        threadLocal.remove();
                    }
                };
            });
            childThreadPool.initialize();
    
            for (int i = 0; i < 10; i++) {
                int finalI = i;
                mainThreadPool.execute(() -> {
                    //模拟在主线程设置上下文变量
                    threadLocal.set(finalI);
                    childThreadPool.execute(() -> System.out.println("子线程" + Thread.currentThread().getName() + " 获取上下文变量=" + threadLocal.get()));
                    threadLocal.remove();
                });
            }
            try {
                childThreadPool.getThreadPoolExecutor().awaitTermination(3, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

     可以看到,我们传递的值都能正确获取到,没有像inheritThreadLocal出现紊乱。

    2020-12-03 10:59:26.350 [main] INFO  o.s.s.c.ThreadPoolTaskExecutor - Initializing ExecutorService
    装饰器中获取到主线程=pool-1-thread-1 获取上下文=0
    装饰器中获取到主线程=pool-1-thread-2 获取上下文=1
    装饰器中获取到主线程=pool-1-thread-3 获取上下文=2
    装饰器中获取到主线程=pool-1-thread-4 获取上下文=3
    装饰器中获取到主线程=pool-1-thread-5 获取上下文=4
    装饰器中获取到主线程=pool-1-thread-6 获取上下文=5
    装饰器中获取到主线程=pool-1-thread-8 获取上下文=7
    装饰器中获取到主线程=pool-1-thread-10 获取上下文=9
    装饰器中获取到主线程=pool-1-thread-9 获取上下文=8
    装饰器中获取到主线程=pool-1-thread-7 获取上下文=6
    
    Disconnected from the target VM, address: '127.0.0.1:4991', transport: 'socket'
    子线程ThreadPoolTaskExecutor-1 获取上下文变量=2
    子线程ThreadPoolTaskExecutor-1 获取上下文变量=3
    子线程ThreadPoolTaskExecutor-1 获取上下文变量=1
    子线程ThreadPoolTaskExecutor-1 获取上下文变量=0
    子线程ThreadPoolTaskExecutor-1 获取上下文变量=5
    子线程ThreadPoolTaskExecutor-1 获取上下文变量=7
    子线程ThreadPoolTaskExecutor-1 获取上下文变量=9
    子线程ThreadPoolTaskExecutor-1 获取上下文变量=8
    子线程ThreadPoolTaskExecutor-1 获取上下文变量=6
    子线程ThreadPoolTaskExecutor-2 获取上下文变量=4
    
    Process finished with exit code 0
    

     

    展开全文
  • 这时候就需要处理子线程与主线程间数据传递的问题。 TaskDecorator 这个问题需要使用线程的ThreadLocal和TaskDecorator来处理。官方文档中描述意思是TaskDecorator是一个执行回调方法的装饰器,主要应用于传
  • 线程池如何传递线程上下文信息

    千次阅读 2020-03-13 22:00:00
    戳蓝字「TopCoder」关注我们哦!业务开发中,一般都会使用ThreadLocal保存一些上下文信息,但是在线程池中执行对应逻辑时,由于是不同线程所...
  • 那么就涉及到线程上下文的问题,配置了线程池,然后设置了线程上下文的数据切换(见下图) ![图片说明](https://img-ask.csdn.net/upload/202007/23/1595495915_606912.png) 然后一个很诡异的问题产生了,比如业务...
  • 在主线程中开启异步线程任务时,主线程的信息时无法直接传递到子线程中,此时需要通过线程池实现上下文信息的传递 案例如下 package com.eno.config.thread; import java.util.Map; import java.util.concurrent...
  • 戳蓝字「TopCoder」关注我们哦!TTL(transmittable-thread-local)是一个线程传递ThreadLocal,异步执行时...
  • jvm之线程上下文加载器与SPI

    万次阅读 2020-07-11 19:05:57
    线程上下文加载器 线程上下文类加载器(Thread Context Class Loader,简称TCCL)是从JDK1.2开始引入的。类java.lang.Thread中的方法getContextClassLoader()和setContextClassLoader(ClassLoader cl)用来获取和设置...
  • 我在使用日志链路追踪的时候(基于SLF4J MDC机制实现日志的链路追踪),我发现使用Hystrix线程池隔离的时候,我不能将子线程没有复制主线程的MDC上下文(Slf4j MDC机制 ),导致日志链路断掉。 问题分析 Hystrix的...
  • 线程上下文安全设计

    千次阅读 2019-07-21 14:34:43
    多个线程进行上下文操作时会引起,线程安全问题,可通过ThreadLocal的特性,为每个线程开启副本,解决此类问题。 存放信息的对象 //存放信息的上下文对象 public class Context { //随便设置几个属性 private ...
  • 1 线程上下文类加载器 2 何时使用Thread.getContextClassLoader()? 3 类加载器与Web容器 4 类加载器与OSGi 总结 1 线程上下文类加载器  线程上下文类加载器(context class loader)是从 JDK 1.2 开始引入的...
  • 主要给大家介绍了关于如何在Spring异步调用中传递上下文的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Spring具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
  • 每个线程都有它自己的一组C P U寄存器,称为线程上下文
  • Java线程上下文类加载器

    千次阅读 2015-01-09 00:01:18
    1 线程上下文类加载器  线程上下文类加载器(context class loader)是从 JDK 1.2 开始引入的。类 java.lang.Thread中的方法 getContextClassLoader()和 setContextClassLoader(ClassLoader cl)用来获取和设置...
  • 异步调用是相对于同步调用而言的,同步调用是指程序按预定顺序一步步执行,每一步必须等到一步执行完后才能执行,异步调用则无需等待一步程序执行完即可执行。异步调用指,在程序在执行时,无需等待执行的返回值...
  • 何时需线程上下文拷贝 比如用户认证和 tracing 调用链相关信息都在请求线程上下文中,但是异步时就会丢失,所以需要一直携带。可实现 spring 的如下接口 TaskDecorator 装饰器的回调接口,该接口将应用于将要执行的...
  • Maven项目,可直接编译通过,已经有一个测试的数据读取 和一个测试的...上下文读取器配置添加至readerPool.readers; 上下文处理器配置添加至serviceProcessors.services; 分析链线程数配置serviceProcessors.threadCount;
  • 在分布式链路跟踪系统中,同一条请求处理链路要用一个全局唯一的 traceId 来标识,那就需要把 traceId 传输到该请求...实际中,同一个系统内部业务处理出现多线程操作时,如果不做显式处理,也容易丢失 Trace 信息。...
  • 调度、线程上下文以及IRQL

    千次阅读 2014-06-29 19:37:56
    线程调度以及线程上下文和当前的IRQL(中断请求级)对于每个处理器上面的驱动程序有很大的影响。而一个线程的调度优先级和处理器的当前IRQL能够决定一个运行的线程能否被中断或者抢占。在抢占式调度过程当中,系统...
  • 导语   在之前的分享中提到过一...那么就需要知道线程上下文,对于线程上下文来讲就是线程的依托。 文章目录什么是上下文 什么是上下文    关于上下文(context),在开发中经常会遇到,例如Spring中的上下文...
  • 如何在 Spring 异步调用中传递上下文

    千次阅读 2019-08-01 22:19:28
    异步调用是相对于同步调用而言的,同步调用是指程序按预定顺序一步步执行,每一步必须等到一步执行完后才能执行,异步调用则无需等待一步程序执行完即可执行。异步调用指,在程序在执行时,无需等待执行的返回值...
  • 1.默认情况下为 THRED Hystirx调用的命令(注解保护的服务调用)都只在一...由于@HystrixCommand注解保护不需要启动一个新线程(不明白),当线程调用超时,父线程中断,导致抛出无法捕获的异常,一般不用. @HystirxCommand...
  • 协程、线程和执行上下文

    千次阅读 2014-11-03 00:16:55
    摘要: 本文介绍协程、线程及它们执行的上下文等概念,同时给出注意事项。协程是用户级的任务调度,线程是内核级的任务调度,而任务调度过程都涉及到上下文切换(保存与恢复),本文将从较为深刻的角度来阐述这些概念...
  • 可怕的线程上下文类装载器(TCCL)

    千次阅读 2017-02-03 15:42:56
    在演讲中我将会提及 Java 的线程上下文类加载器(TCCL),但是整个演讲只有 25 分钟,我没有更多时间对此进行深入讨论。所以我希望写这篇博客能够帮助大家了解到一些相关背景信息。本文中的很多技术信息和研究取自于 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 109,452
精华内容 43,780
关键字:

新起线程传递上下文