java线程池增强实现

       前两天在开涛的公众号里,开涛聊到一次请求生成唯一的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中,`ThreadPoolExecutor` 和 `ScheduledExecutorService` 是用于管理线程池并执行异步任务的关键组件。通过合理配置这些类,可以有效地控制并发任务的执行。 #### 创建线程池实例 为了创建一个高效的线程池,通常建议根据系统的硬件资源(如CPU核心数量)来设定合理的参数: ```java import java.util.concurrent.*; public class AsyncExample { private static final int CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors(); private static final int MAX_POOL_SIZE = CORE_POOL_SIZE * 2; private static final BlockingQueue<Runnable> WORK_QUEUE = new LinkedBlockingQueue<>(5); public static void main(String[] args) throws InterruptedException { // 定义线程工厂以增加可追踪性 ThreadFactory namedThreadFactory = new ThreadFactoryBuilder() .setNameFormat("async-task-%d").build(); // 设置拒绝策略为 CallerRunsPolicy RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy(); ExecutorService threadPool = new ThreadPoolExecutor( CORE_POOL_SIZE, MAX_POOL_SIZE, 60L, TimeUnit.SECONDS, WORK_QUEUE, namedThreadFactory, handler); // 提交异步任务给线程池 submitTasks(threadPool); // 关闭线程池 shutdownThreadPool(threadPool); } private static void submitTasks(ExecutorService pool){ for(int i=0;i<10;i++){ final int taskId=i; pool.submit(() ->{ System.out.println("Task "+taskId+" is running on thread "+Thread.currentThread().getName()); try{TimeUnit.SECONDS.sleep(2);}catch(Exception ignore){} System.out.println("Task "+taskId+" has finished."); }); } } private static void shutdownThreadPool(ExecutorService executorService)throws InterruptedException{ executorService.shutdown(); // Disable new tasks from being submitted try { // Wait a while for existing tasks to terminate if (!executorService.awaitTermination(800, TimeUnit.MILLISECONDS)) { executorService.shutdownNow(); // Cancel currently executing tasks } } catch (InterruptedException ie) { // Preserve interrupt status Thread.currentThread().interrupt(); } } } ``` 上述代码展示了如何构建一个具有特定属性的线程池,并向其提交多个简单的打印任务[^4]。值得注意的是,在实际应用环境中应当更加谨慎地设计错误处理逻辑以及确保资源能够得到妥善释放。 对于更复杂的场景下需要获取异步操作的结果时,则推荐使用`CompletableFuture`配合线程池一起工作。这不仅简化了编码过程还增强了灵活性。 ```java // 使用CompletableFuture结合自定义线程池执行带返回值的任务 CompletableFuture.supplyAsync(() -> { // 执行一些耗时的操作... return "Result"; }, threadPool).thenAccept(result -> { System.out.println("Received result: " + result); }); ``` 此段代码片段说明了利用`supplyAsync()` 方法可以在指定的线程池里启动带有返回值得异步计算流程,并且可以通过链式调用来进一步处理最终获得的数据[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值