0

我在@Async方法注释中遇到了某种线程问题,其中一个参数包含一个List枚举并且正在删除项目。清单很小,只有 2 项。物品的掉落不是立即的,但有时需要数小时或数天才能出现。

这是我们程序的一般流程:

A在其方法中Controller生成上述,将列表传递给一个类,该类调用数据库进行批处理,并为数据库中的每个项目触发一个事件,传递列表。这个列表最终被传递到一个方法中,该方法然后删除第一个项目或两个项目。List@RequestMappingService@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它遍历一批数据,一遍又一遍地发送相同的列表。另一个类中有一个 aync 方法来测试列表是否相同。我无法在本地复制该问题,但这是核心问题。

据我所知,Java 是按引用传递的,传递给方法的每个变量都会在堆栈中获得自己的指针,指向内存中的该引用,但不要认为它会导致我们耗尽内存。我们在 PCF 中运行,在此期间看不到任何内存峰值或任何东西。内存保持在 50% 左右。我也尝试使用CopyOnWriteArrayList(线程安全)代替,ArrayList但问题仍然存在。

问题:

知道为什么该@Async方法会在方法参数中删除项目吗?列表在构建后永远不会被修改,那么为什么项目会消失呢?为什么第一项总是消失?为什么不是第二项?为什么两者都会消失?


编辑:所以这个问题最终没有什么关系@Async。我发现深度嵌套的代码会从列表中删除项目,导致项目丢失。

4

1 回答 1

1

你说的没错,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.

于 2020-02-15T19:17:21.047 回答