У меня возникла проблема с потоком в аннотации метода @Async
, где один аргумент содержит перечисление List
и отбрасывает элементы. Список очень маленький, 2 предмета. Отбрасывание предметов происходит не сразу, но иногда для его появления требуются часы или дни.
Это общий поток нашей программы:
A Controller
создает Сказал List
в своем методе @RequestMapping
, передает список классу Service
, который выполняет вызов базы данных для пакетной обработки и запускает событие для каждого элемента из базы данных, передавая список. Этот список в конечном итоге передается в метод @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
, упомянутые ранее. Он вращается через пакет данных, отправляя один и тот же список снова и снова. В другом классе есть метод ayn c, чтобы проверить, что список такой же. Я не смог воспроизвести проблему локально, но это основная проблема.
Насколько мне известно, Java является передачей по ссылке, и каждая переменная, передаваемая в метод, получает свой собственный указатель в стеке. на эту ссылку в памяти, но не думаю, что это заставит нас исчерпать память. Мы работаем в PCF и не видим скачков памяти или чего-либо еще в течение этого времени. Память постоянна около 50%. Я также попытался использовать CopyOnWriteArrayList
(потокобезопасный) вместо ArrayList
, но проблема все еще существует.
Вопросы:
Любая идея, почему @Async
метод будет отбрасывать элементы в аргументе метода? Список никогда не изменяется после создания, так почему же предметы исчезают? Почему первый элемент всегда исчезает? Почему не второй предмет? Почему оба исчезли бы?
Редактировать: Таким образом, этот вопрос не имел ничего общего с @Async
в конце. Я нашел глубоко вложенный код, который удалял элементы из списка, в результате чего элементы go отсутствовали.