前两天在开涛的公众号里,开涛聊到一次请求生成唯一的traceId在各个业务系统中传递,然后通过日志收集各个业务服务中的日志,形成一次请求的完整日志。开涛简单的提到了是使用自己实现的线程池增强技术来传递traceId。
我这边系统也有类似的需求。所以我就尝试性地实现了下线程池增强。本来想着既然是增强,第一反应是用代理技术去实现,后来发现不需要代理就可以简单地实现。
我大致的场景是把线程中附带的用户信息从当前线程传递到要开启的新的线程中去,并告诉MDC进行日志打印。
下面简单介绍下代码的实现:
首先是自定义的线程池UserContextThreadPoolExecutor.java类,继承了jdk并发包中的ThreadPoolExecutor类:
package com.onlyou.framework.ext.concurrent; import com.onlyou.olyfinance.common.UserContext; import java.util.concurrent.*; public class UserContextThreadPoolExecutor extends ThreadPoolExecutor { public UserContextThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); } public UserContextThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory); } public UserContextThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler); } public UserContextThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler); } /** * 重写执行线程实例的方法 * @param command */ public void execute(Runnable command) { if (command == null){ throw new NullPointerException(); } UserContextRunnableTask task =new UserContextRunnableTask(); task.setUser(UserContext.getUser()); task.setDelegate(command); super.execute(task); } }
UserContext.getUser()是从线程中获取到用户信息,实际上扔给线程池的类是UserContextRunnableTask类:
package com.onlyou.framework.ext.concurrent; import com.onlyou.olyfinance.application.vo.UserInfo; import com.onlyou.olyfinance.common.UserContext; public class UserContextRunnableTask implements Runnable{ private UserInfo user; private Runnable delegate; @Override public void run() { UserContext.setUser(user); UserContext.setMDCBeforeInvoke(); try{ delegate.run(); }catch (Throwable e){ throw e; }finally { UserContext.removeUser(); UserContext.removeMDCAfterInvoke(); } } public void setUser(UserInfo user) { this.user = user; } public void setDelegate(Runnable delegate) { this.delegate = delegate; } }
多线程中要执行的业务内容是UserContextRunnableTask 中的Runnable delegate去执行的delegate.run()。而真正线程池执行的是UserContextRunnableTask.run()。 在UserContextRunnableTask的run()方法中我们UserContext.setUser(user)里我们把用户信息绑定到当前线程中(基于线程变量),并在线程执行完UserContext.removeUser()把用户信息从当前线程中移除。
这里可能有人会有疑问,我只是重写了执行Runnable接口的多线程实例。那JDK1.5后出来的callable接口呢。其实callable接口的实例扔给线程池,线程池会调用这个方法:
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { return new FutureTask<T>(callable); }
返回一个FutureTask实例给线程池。这个FutureTask其实就是实现了Runnable接口。在FutureTask中的run()方法中:
public void run() { if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread())) return; try { Callable<V> c = callable; if (c != null && state == NEW) { V result; boolean ran; try { result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result); } } finally { // runner must be non-null until state is settled to // prevent concurrent calls to run() runner = null; // state must be re-read after nulling runner to prevent // leaked interrupts int s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } }
在FutureTask的run()方法中,会去调用callable.call()并做一些额外的处理, result = c.call(),result会赋值给FutureTask的outcome属性。outcome就是返回值,用get()即可以获取。
相关推荐
Java线程池是Java并发编程中的重要组成部分,它在多线程编程中扮演着至关重要的角色,有效地管理和调度线程资源,提高了程序的性能和稳定性。本文将深入探讨Java线程池的概念、工作原理以及如何在实际开发中运用。 ...
Java语言游戏项目实战资源包 内容概览: 这次分享为你带来了丰富的Java语言游戏项目实战资源,让你在实践中深入掌握Java语言,并开启游戏开发之旅。资源包中包括: 游戏项目代码:精心挑选了多个经典的小游戏项目...
Java线程池是一种高效利用系统资源,管理和控制并发任务执行的工具。在JDK1.5及以后的版本中,`ExecutorService`作为线程池的主要接口,提供了基础的线程池服务。然而,标准的`ExecutorService`线程池在特定场景下...
总结来说,Java线程池通过复用线程提高了系统的并发性能和资源利用率,而Lambda表达式则简化了代码编写,增强了代码的可读性和表达力。两者结合,能够帮助开发者编写出更加高效、易维护的多线程程序。在实际开发中,...
5. **线程池**:Java的`ExecutorService`和`ThreadPoolExecutor`是线程池的核心实现。线程池可以预先创建一定数量的线程,避免频繁创建和销毁线程带来的开销。线程池可通过`newFixedThreadPool`、`...
它包括了各种线程池实现,如ThreadPoolExecutor,它是Java中最常用的线程池。线程池允许开发者管理和复用已创建的线程,提高系统效率,避免频繁创建和销毁线程带来的开销。 **ThreadPoolExecutor** 是Java并发库中...
Java实现QQ聊天是一个涵盖多个技术领域的复杂项目,主要涉及到网络通信、多线程编程、对象序列化、图形用户界面(GUI)设计等多个方面。这里我们将深入探讨这些关键知识点。 首先,Java是编程语言,它的跨平台特性...
在Java中,`java.util.concurrent`包提供了`ExecutorService`和`ThreadPoolExecutor`接口及实现,用于创建和管理线程池。开发者可以通过配置线程池的参数,如核心线程数、最大线程数、线程存活时间、工作队列大小等...
对线程池 ThreadPoolExecutor 做一些扩展增强,主要实现以下目标:实现对运行中线程池参数的动态修改,实时生效。实时监控线程池的运行状态,触发设置的报警策略时报警,报警信息推送办公平台。定时采集线程池指标...
即时聊天系统设计与实现是计算机科学领域中一个常见的项目,特别是在Java编程语言中。这个项目通常涉及网络编程、多线程、并发处理以及用户界面设计等多个核心知识点。下面,我们将详细探讨这些关键概念。 首先,...
"java泛型"是Java 5引入的新特性,它增强了类型安全并减少了强制类型转换的需要。泛型允许我们在类、接口和方法中使用类型参数,从而可以在编译时检查类型安全。了解和熟练运用泛型,可以提升代码的可读性和复用性。...
Java是一种广泛使用的面向对象的编程语言,以其跨平台性、高效性和强大的库支持而闻名。...通过深入学习和实践,读者不仅可以提升Java编程技能,还能增强对并发编程和ORM的理解,从而在软件开发领域更上一层楼。
Java 实现FTP(File Transfer Protocol)是指使用Java编程语言来创建一个FTP服务器或者客户端,以进行文件的上传、下载和管理。FTP是一种广泛使用的互联网协议,用于在不同计算机之间传输文件。在Java中,我们可以...
5. **集合框架**:Java集合框架包括List、Set、Map接口及其实现类,如ArrayList、LinkedList、HashSet、HashMap等,以及迭代器(Iterator)的使用。 6. **IO流**:Java的输入/输出流(IO流)系统用于读写文件、网络...
7. **设计模式**:在实现购票界面时,可以运用工厂模式简化组件的创建,单例模式确保线程池或数据库连接的唯一性,观察者模式用于界面状态的实时更新等。 通过上述知识点,我们可以构建出一个功能完善的车票购买...
Java中,装饰模式常用于不修改原有代码的情况下增强对象功能。 10. 外观模式(Facade):为子系统提供一个统一的接口,使得这些子系统更加容易被使用。在Java中,外观模式可以简化客户端对复杂系统的调用。 11. 享...
【标题】"01_黑马程序员_张孝祥_Java基础加强_课程价值与目标介绍.zip" 提供的是一门由黑马程序员机构推出的Java基础强化课程,由讲师张孝祥主讲,旨在深入讲解Java编程的基础知识并进行能力提升。 【描述】中提到...
标题中的“纯java实现 兼容所有Java平台以及Java语言的引擎”暗示了这是一个使用Java编程语言开发的软件引擎,其设计目标是能够在任何支持Java的平台上运行,无论是桌面环境、移动设备还是云端服务器。这样的引擎...
1. **自定义线程池**:可能提供了定制化的线程池实现,具有特定的行为或者优化,比如更好的线程管理策略,或者针对Android平台的特定优化。 2. **增强的并发集合**:可能扩展了标准的并发集合类,增加了额外的功能...