为什么 Spring Boot @Async 在我的 List 参数中删除项目?
Why is Spring Boot @Async dropping items in my List argument?
我在 @Async
方法注释中遇到某种线程问题,其中一个参数包含 List
枚举并且正在丢弃项目。该列表非常小,只有 2 项。物品掉落不是立即出现的,但有时需要数小时或数天才能出现。
这是我们程序的大致流程:
A Controller
在其 @RequestMapping
方法中生成所述 List
,将列表传递给 Service
class,后者调用用于批处理的数据库,并为数据库中的每个项目触发一个事件,传递列表。这个列表最终被传递给一个 @Async
方法,然后丢弃第一个项目或两个项目。
Controller.methodA()
-> Creates list with two items in it
-> Calls void Service.methodX(list)
-> Load batch from database
-> Iterate over batch
-> Print items from list --- list in tact
-> Calls void AsyncService.asyncMethod(list)
-> Print items from list --- eventually drops items here always the first item, sometimes both.
代码配置和基本示例:
我们将其配置为有 2 个线程:
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setMaxPoolSize(5); // Never actually creates 5 threads
threadPoolTaskExecutor.setCorePoolSize(2); // Only 2 threads are ever created
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
}
这是一个本地副本,试图触发核心问题,但没有运气:
@RestController
public class ThreadingController {
private final ThreadingService threadingService;
public ThreadingController(ThreadingService threadingService) {
this.threadingService = threadingService;
}
@GetMapping("/test")
public void testThreads() {
List<SomeEnum> list = new ArrayList<>();
list.add(SomeEnum.FIRST_ENUM);
list.add(SomeEnum.SECOND_ENUM);
for (int i = 0; i < 1000; i++) {
this.threadingService.workSomeThreads(i, list);
}
}
}
public enum SomeEnum {
FIRST_ENUM("FIRST_ENUM"),
SECOND_ENUM("SECOND_ENUM");
@Getter
private String name;
SomeEnum(String name) {
this.name = name;
}
}
@Slf4j
@Service
public class ThreadingService {
@Async
public void workSomeThreads(int i, List<SomeEnum> list) {
try {
Thread.sleep(100L); // Add some delay to slow things down to trigger GC or other tests during processing
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("Count {} ; Here are the list items: {}", i, list.toString());
assert(list.size() == 2);
}
}
如果我们仔细看一下,我有一个控制器可以模拟前面提到的 Controller
和 Service
。它循环处理一批数据,一遍又一遍地发送相同的列表。另一个 class 中有一个 aync 方法来测试列表是否相同。我无法在本地复制问题,但这是核心问题。
据我所知,Java 是按引用传递的,传递给方法的每个变量在堆栈中都有自己的指针,指向内存中的那个引用,但不要认为这会导致我们运行 内存不足。我们在 PCF 运行ning 期间没有看到任何内存峰值或任何东西。内存恒定在 50% 左右。我也尝试使用 CopyOnWriteArrayList
(线程安全)而不是 ArrayList
,但问题仍然存在。
问题:
知道为什么 @Async
方法会丢弃方法参数中的项目吗?该列表在构造后永远不会修改,那么为什么项目会消失?为什么第一项总是消失?为什么不是第二项?为什么两者都会消失?
Edit: So this question had little to do with @Async
in the end. I found deeply nested code that removed items from the list, causing items to go missing.
你说的对,Java确实是传参。列表中的更改肯定是由于在线程执行时修改此列表的其他一些代码造成的。没有其他方法可以改变对象的值。
您必须调查以下部分中的代码,以确定是否有某些内容正在修改列表。
-> Print items from list --- eventually drops items here always the first item, sometimes both.
-> code following this might be changing the list.
由于 AsyncService 将异步执行其代码,同时,一些其他代码会修改列表。
你不妨将方法参数设置为final
。
我在 @Async
方法注释中遇到某种线程问题,其中一个参数包含 List
枚举并且正在丢弃项目。该列表非常小,只有 2 项。物品掉落不是立即出现的,但有时需要数小时或数天才能出现。
这是我们程序的大致流程:
A Controller
在其 @RequestMapping
方法中生成所述 List
,将列表传递给 Service
class,后者调用用于批处理的数据库,并为数据库中的每个项目触发一个事件,传递列表。这个列表最终被传递给一个 @Async
方法,然后丢弃第一个项目或两个项目。
Controller.methodA()
-> Creates list with two items in it
-> Calls void Service.methodX(list)
-> Load batch from database
-> Iterate over batch
-> Print items from list --- list in tact
-> Calls void AsyncService.asyncMethod(list)
-> Print items from list --- eventually drops items here always the first item, sometimes both.
代码配置和基本示例:
我们将其配置为有 2 个线程:
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setMaxPoolSize(5); // Never actually creates 5 threads
threadPoolTaskExecutor.setCorePoolSize(2); // Only 2 threads are ever created
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
}
这是一个本地副本,试图触发核心问题,但没有运气:
@RestController
public class ThreadingController {
private final ThreadingService threadingService;
public ThreadingController(ThreadingService threadingService) {
this.threadingService = threadingService;
}
@GetMapping("/test")
public void testThreads() {
List<SomeEnum> list = new ArrayList<>();
list.add(SomeEnum.FIRST_ENUM);
list.add(SomeEnum.SECOND_ENUM);
for (int i = 0; i < 1000; i++) {
this.threadingService.workSomeThreads(i, list);
}
}
}
public enum SomeEnum {
FIRST_ENUM("FIRST_ENUM"),
SECOND_ENUM("SECOND_ENUM");
@Getter
private String name;
SomeEnum(String name) {
this.name = name;
}
}
@Slf4j
@Service
public class ThreadingService {
@Async
public void workSomeThreads(int i, List<SomeEnum> list) {
try {
Thread.sleep(100L); // Add some delay to slow things down to trigger GC or other tests during processing
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("Count {} ; Here are the list items: {}", i, list.toString());
assert(list.size() == 2);
}
}
如果我们仔细看一下,我有一个控制器可以模拟前面提到的 Controller
和 Service
。它循环处理一批数据,一遍又一遍地发送相同的列表。另一个 class 中有一个 aync 方法来测试列表是否相同。我无法在本地复制问题,但这是核心问题。
据我所知,Java 是按引用传递的,传递给方法的每个变量在堆栈中都有自己的指针,指向内存中的那个引用,但不要认为这会导致我们运行 内存不足。我们在 PCF 运行ning 期间没有看到任何内存峰值或任何东西。内存恒定在 50% 左右。我也尝试使用 CopyOnWriteArrayList
(线程安全)而不是 ArrayList
,但问题仍然存在。
问题:
知道为什么 @Async
方法会丢弃方法参数中的项目吗?该列表在构造后永远不会修改,那么为什么项目会消失?为什么第一项总是消失?为什么不是第二项?为什么两者都会消失?
Edit: So this question had little to do with
@Async
in the end. I found deeply nested code that removed items from the list, causing items to go missing.
你说的对,Java确实是传参。列表中的更改肯定是由于在线程执行时修改此列表的其他一些代码造成的。没有其他方法可以改变对象的值。 您必须调查以下部分中的代码,以确定是否有某些内容正在修改列表。
-> Print items from list --- eventually drops items here always the first item, sometimes both.
-> code following this might be changing the list.
由于 AsyncService 将异步执行其代码,同时,一些其他代码会修改列表。
你不妨将方法参数设置为final
。