在Spring Boot中,可以通过使用@Async注解和ThreadPoolTaskExecutor线程池来实现异步调用自定义方法。 具体步骤如下:
1.在Spring Boot启动类上添加@EnableAsync注解来开启异步任务的支持:
@SpringBootApplication
@EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
2.在需要调用自定义方法的方法上使用@Async注解来表明这是一个异步方法:
@Service
public class UserService {
@Async("myTaskExecutor")
public void doSomeAsyncTask() {
// 在这里编写需要异步执行的代码
}
}
3.在配置类中定义一个ThreadPoolTaskExecutor线程池,并在@Bean注解上指定执行结束后的回调方法名:
@Configuration
public class ThreadConfig {
@Bean("myTaskExecutor")
public ThreadPoolTaskExecutor myTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(1000);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setThreadNamePrefix("myTaskExecutor-");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(60);
executor.setTaskDecorator(new ContextDecorator());
executor.setBeanName("myTaskExecutorBeanName");
executor.initialize();
executor.setTaskDecorator(new ContextDecorator());
executor.setThreadNamePrefix("async-task-executor-");
executor.initialize();
executor.setTaskDecorator((runnable) -> {
// 在任务执行结束后调用回调方法
return () -> {
try {
runnable.run();
} finally {
System.out.println("任务执行结束,调用自定义回调方法");
}
};
});
return executor;
}
/**
* 定义任务执行结束后的回调方法
*/
public void taskCompleteCallback(Runnable runnable, Throwable throwable) {
if (throwable == null) {
// 执行成功
System.out.println("任务执行成功");
} else {
// 执行失败
System.out.println("任务执行失败");
}
}
}
在上面这个配置方法中,我们定义了一个ThreadPoolTaskExecutor线程池,并在@Bean注解上指定了线程池的名称。然后,在setTaskDecorator方法中,我们为线程池的任务添加了一个装饰器,用来在任务执行结束后调用回调方法。
最后,我们只需要在配置类中定义一个名为taskCompleteCallback的方法,该方法的参数必须是Runnable和Throwable类型,用来接收执行结束后的回调信息。
线程从ThreadLocal获取值时,会优先从自己的ThreadLocalMap中取值。如果当前线程没有设置该ThreadLocal,则会向上遍历当前线程的父线程,直到找到父线程的ThreadLocalMap中存储的值或者到达顶层线程。如果最终都没有找到,则返回ThreadLocal的初始值。
因此,每个线程之间的ThreadLocal是独立的,互不干扰,线程之间ThreadLocal的值互相不可见。
举个例子,假设有一个父线程P和一个子线程C。在父线程P中设置了一个ThreadLocal,并赋值为"value1"。然后,在子线程C中也设置了一个同名的ThreadLocal,并将其值设置为"value2"。当在子线程C中访问这个ThreadLocal时,会优先从子线程C的ThreadLocalMap中获取其值,即"value2"。如果子线程C中的ThreadLocalMap没有该值,则会向上遍历到父线程P的ThreadLocalMap中获取值,即"value1"。如果再向上遍历,最后会到达顶层线程中的ThreadLocalMap,如果仍然没有,则返回初始值。 在Java中,ThreadLocal的初始值是由initialValue()方法提供的。当从ThreadLocal获取值时,如果当前线程没有设置ThreadLocal的值,会调用initialValue()方法进行初始化,返回初始值,并存储到当前线程的ThreadLocalMap中。如果没有提供initialValue()方法,初始值为null。
举个例子,如果我们定义了一个ThreadLocal变量:
ThreadLocal<Integer> intThreadLocal = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 0;
}
};
这里通过重写ThreadLocal的initialValue()方法,将ThreadLocal的初始值设置为0。
当我们在某个线程中调用get()方法获取ThreadLocal的值时,如果该线程还没有设置过该ThreadLocal的值(即ThreadLocalMap中不存在该ThreadLocal的值),则会调用initialValue()方法,返回初始值0,并存储到该线程的ThreadLocalMap中。后续再次从该线程中获取该ThreadLocal的值时,就会直接从ThreadLocalMap中获取,而不会再调用initialValue()方法了。
ThreadLocal的get()方法会先从当前线程中的ThreadLocalMap中查找该ThreadLocal对象的值,如果当前线程没有设置该ThreadLocal的值,则会向上遍历当前线程的父线程,直到找到父线程的ThreadLocalMap中存储的值或者到达顶层线程。如果最终都没有找到,则会返回ThreadLocal的初始值。
具体来说,get()方法会先获取当前线程的ThreadLocalMap对象,并以该ThreadLocal对象为key查询ThreadLocalMap中是否存在相应的值。如果存在则直接返回,否则会尝试从父线程的ThreadLocalMap中查询,直到查询到顶级线程的ThreadLocalMap中仍然没有找到该ThreadLocal对象对应的值,此时会返回ThreadLocal的初始值。
需要注意的是,虽然ThreadLocal会从父线程中继承变量的值,但默认情况下,ThreadLocal的变量是不可见的,也就是说,每个线程有自己的ThreadLocalMap,线程之间是相互独立的。如果需要在父子线程之间共享ThreadLocal变量,需要使用共享ThreadLocal变量的工具类,如InheritableThreadLocal。
InheritableThreadLocal对象只是在子类get()的时候从父类复制已经的数据,之后子类再进行增删查操作时,只会影响当前线程对象,对于使用线程池的线程对象来说,重复使用线程对象时,该线程对象即使remove()之后,再get(),也无法继承父类了,因为该线程没有销毁,当前的ThreadLocalMap的key还存在!