前两天在开涛的公众号里,开涛聊到一次请求生成唯一的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语言,并开启游戏开发之旅。资源包中包括: 游戏项目代码:精心挑选了多个经典的小游戏项目...
然而,自JDK 1.5开始,随着`java.util.concurrent`包的引入,Java线程池的功能得到了极大的增强,为开发者提供了更加高效且易于管理的线程管理方式。 #### 二、线程池的作用与必要性 线程池的主要功能在于限制系统...
Java线程池是一种高效利用系统资源,管理和控制并发任务执行的工具。在JDK1.5及以后的版本中,`ExecutorService`作为线程池的主要接口,提供了基础的线程池服务。然而,标准的`ExecutorService`线程池在特定场景下...
Java线程池提供了丰富的策略和参数,以满足不同场景下的需求。开发者可以根据应用的特性,合理配置线程池,优化并发处理能力,提高系统的响应速度和稳定性。在Java中,除了ArrayBlockingQueue之外,还有其他的队列...
总结来说,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多线程技术,我们能够实现远程文件的无阻塞读取,这不仅提高了程序的响应速度,还增强了整体系统的稳定性。未来还可以考虑加入更多的优化措施来进一步提升性能。
基于JAVA实现的源代码搜索引擎Hicode,不仅提供了高效的源代码搜索功能,还通过集成Liferay门户增强了用户体验。未来,可以通过引入更多高级功能(如代码片段的相似度计算、代码质量评估等)进一步提升系统的实用...
Java是一种广泛使用的面向对象的编程语言,以其跨平台性、高效性和强大的库支持而闻名。...通过深入学习和实践,读者不仅可以提升Java编程技能,还能增强对并发编程和ORM的理解,从而在软件开发领域更上一层楼。
在IT行业中,线程池是高并发编程领域中不可或缺的一部分,尤其在...合理使用线程池不仅可以提升系统性能,还能增强程序的可维护性和稳定性。在设计和实现并发系统时,应充分考虑线程池的配置和管理,以达到最佳效果。
Java 实现FTP(File Transfer Protocol)是指使用Java编程语言来创建一个FTP服务器或者客户端,以进行文件的上传、下载和管理。FTP是一种广泛使用的互联网协议,用于在不同计算机之间传输文件。在Java中,我们可以...
5. **集合框架**:Java集合框架包括List、Set、Map接口及其实现类,如ArrayList、LinkedList、HashSet、HashMap等,以及迭代器(Iterator)的使用。 6. **IO流**:Java的输入/输出流(IO流)系统用于读写文件、网络...
7. **设计模式**:在实现购票界面时,可以运用工厂模式简化组件的创建,单例模式确保线程池或数据库连接的唯一性,观察者模式用于界面状态的实时更新等。 通过上述知识点,我们可以构建出一个功能完善的车票购买...