Виснет BackgroundTask в случайном месте

есть проблема, создается такой BackgroundTask, запускается и периодически виснет в рандомном месте. сегодня из четырех запусков два раза зависал после строк
taskLog.debug("Снимаем признак со счетов ");
и
taskLog.debug("Создаем info " + processedCount + "+ для счетов, у которых info нет. Количество: " + billsWithoutInfo.size());
ошибок никаких не выдавал, с базой не работал, блокировок в базе не было. что может быть?
такая ситуация не первый раз, предыдущие два месяца тоже зависал в рандомном месте и приходилось перезапускать

new BackgroundTask<>(25L, TimeUnit.MINUTES, this) {
            @Override
            public Void run(TaskLifeCycle taskLifeCycle) throws Exception {
                Logger taskLog = LoggerFactory.getLogger(this.getClass());
                taskLog.debug(String.format("Начинаем закрытие периода. Счетов всего: %d", billsCount));
                int processedCount = 0;
                while (true) {
                    BigDecimal iterationCount = BigDecimal.ZERO.setScale(1, RoundingMode.UNNECESSARY);
                    taskLog.debug("Загружаем счета " + processedCount + "+");
                    List<Bill> bills = billService.getBillsForClosingPeriod(period, 100);
                    taskLog.debug("Счетов загружено " + bills.size());
                    if (bills.isEmpty()) {
                        taskLog.debug("Цикл окончен, выходим");
                        break;
                    }
                    taskLog.debug("Снимаем признак со счетов ");
                    for (Bill bill : bills) {
                        bill.setReadyToSendTo1c(true);
                        iterationCount = iterationCount.add(BigDecimal.valueOf(0.5));
                        if (iterationCount.remainder(BigDecimal.ONE).compareTo(new BigDecimal(0.5).setScale(1)) < 0)
                            taskLifeCycle.publish(processedCount + iterationCount.intValue());
                    }
                    taskLog.debug("Сохраняем счета " + processedCount + "+");
                    billService.saveBills(bills);
                    taskLog.debug("Счета " + processedCount + "+ сохранены");
                    iterationCount = new BigDecimal(bills.size() / 2).setScale(1, RoundingMode.UNNECESSARY);
                    if (iterationCount.remainder(BigDecimal.ONE).compareTo(new BigDecimal(0.5).setScale(1)) < 0)
                        taskLifeCycle.publish(processedCount + iterationCount.intValue());

                    List<String> numbers = bills.stream().map(Bill::getDocumentNumber).collect(Collectors.toList());
                    taskLog.debug("Загружаем info " + processedCount + "+");
                    List<BillInfo1C> infos = billService.get1CInfoByBillNumbers(numbers);
                    taskLog.debug("Info загружены " + infos.size() + ". Снимаем признак.");
                    for (BillInfo1C info : infos) {
                        info.setReadyToSendTo1c(true);
                        iterationCount = iterationCount.add(BigDecimal.valueOf(0.5));
                        if (iterationCount.remainder(BigDecimal.ONE).compareTo(new BigDecimal(0.5).setScale(1)) < 0)
                            taskLifeCycle.publish(processedCount + iterationCount.intValue());
                    }
                    Set<String> doneNumbers = infos.stream().map(BillInfo1C::getBillNumberLK).collect(Collectors.toSet());
                    List<Bill> billsWithoutInfo = bills.stream().filter(bill -> !doneNumbers.contains(bill.getDocumentNumber())).collect(Collectors.toList());
                    taskLog.debug("Создаем info " + processedCount + "+ для счетов, у которых info нет. Количество: " + billsWithoutInfo.size());
                    for (Bill bill : billsWithoutInfo) {
                        Map<String, Object> params = new HashMap<>();
                        params.put("billNumberLK", bill.getDocumentNumber());
                        params.put("period", bill.getPeriod());
                        params.put("inn", bill.getContragent().getInn());
                        params.put("contractNumber", bill.getContract().getMainContract() != null ?
                                bill.getContract().getMainContract().getNumber() :
                                bill.getContract().getNumber());
                        params.put("sum", bill.getSum());
                        params.put("readyToSendTo1c", true);
                        BillInfo1C info = billService.createInfoFrom1c(params,
                                false);
                        infos.add(info);
                        iterationCount = iterationCount.add(BigDecimal.valueOf(0.5));
                        if (iterationCount.remainder(BigDecimal.ONE).compareTo(new BigDecimal(0.5).setScale(1)) < 0)
                            taskLifeCycle.publish(processedCount + iterationCount.intValue());
                    }
                    taskLog.debug("Сохраняем info " + processedCount + "+. Количество: "+ infos.size());
                    billService.saveBillInfos(infos);
                    taskLog.debug("Info " + processedCount + "+ сохранены");
                    iterationCount = new BigDecimal(bills.size()).setScale(1, RoundingMode.UNNECESSARY);
                    taskLifeCycle.publish(processedCount + iterationCount.intValue());

                    processedCount += iterationCount.intValue();
                }
                //для верности, если были погрешности и processingCount < total
                taskLifeCycle.publish(billsCount);
                return null;
            }

            @Override
            public void done(Void result) {
                calculationConfig.setClosedPeriod(closingPeriod);
                close(StandardOutcome.CLOSE);
            }

            @Override
            public boolean handleException(Exception ex) {
                Logger taskLog = LoggerFactory.getLogger(this.getClass());
                taskLog.error("Ошибка при закрытии периода", ex);
                ex.printStackTrace();
                notifications.create(Notifications.NotificationType.ERROR)
                        .withCaption("При закрытии периода произошла ошибка")
                        .show();
                return true;
            }
        }

Зависания нужно продиагностировать. С ходу по коду не видно, что там может зависнуть. Если есть доступ к Java процессу, то либо снять дамп потоков утилитой командной строки jstack, либо подключиться к процессу VisualVM, и там тоже есть дамп потоков.
Когда у вас будет дамп потоков, вы увидите, где виснет, и уже можно будет думать о решении проблемы.

https://visualvm.github.io/

1 симпатия

спасибо, посмотрю