Using Java stream forEach() in ScheduledExecutorService freezes



The general idea is to have a Runnable running every 10 seconds in background to check some data and if needed make changes in an object. ScheduledExecutorService is instantiated in method main() and the task is scheduled. Runnable task instantiates Crawler object and starts crawling. Most of the times it runs couple of times with success but when application is running and data changes one of crawler’s method is fired but never ends. There is no loop in the code. I was trying to debug also without success. Maybe you will be able to spot where the problem lays.

Main:

public class Main {

    public static void main(String[] args) {

        DataStock dataStock = DataStock.getInstance();
        ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();
        ses.scheduleAtFixedRate(new EveryFiveSeconds(), 5, 5, TimeUnit.SECONDS);
        // below the task which fails after couple of runs
        ses.scheduleAtFixedRate(new EveryTenSeconds(), 1 , 10, TimeUnit.SECONDS);
        dataStock.init();

        Menu currentScreen = new UserMenu();
        while(currentScreen != null) {
            currentScreen = currentScreen.display();
        }

    }
}

EveryTenSeconds Runnable:

public class EveryTenSeconds implements Runnable {
    @Override
    public void run() {
        Crawler crawler = new Crawler();
        crawler.crawl();
    }
}

Crawler:

public class Crawler {

    private final DataStock dataStock;

    public Crawler() {
        this.dataStock = DataStock.getInstance();
    }

    public void crawl() {
        checkOutRentables(dataStock.getCarServicesWithOwners().keySet());
        checkFinancialBook(dataStock.getPaymentsBook(), dataStock.getCurrentDate());
    }

    private void checkOutRentables(Set<CarService> carServices) {
        System.out.println("Start check...");
        carServices.stream()
                .flatMap(service -> service.getWarehousesSet().stream())
                .filter(rentable -> !rentable.isAvailableForRent())
                .forEach(RentableArea::refreshCurrentState);
        System.out.println("Checking finished");
    }

    private void checkFinancialBook(Set<BookEntry> bookEntries, LocalDate currentDate) {
        System.out.println("Start second check...");
        bookEntries.stream()
                .filter(bookEntry -> currentDate.isAfter(bookEntry.getPaymentDeadline()) && !bookEntry.isPaid() && !bookEntry.isNotified())
                .forEach(BookEntry::notifyDebtor);
        System.out.println("Finished second check..."); //this line never shows in one of runs and the task is never repeated again...

    }
}

BookEntry

public class BookEntry {
    private final UUID rentableId = UUID.randomUUID();
    private final UUID personId;
    private final UUID id;
    private final BigDecimal amountDue;
    private final LocalDate paymentDeadline;
    private boolean paid = false;
    private boolean notified = false;

    public BookEntry(UUID personId, UUID id, BigDecimal amountDue, LocalDate paymentDeadline) {
        this.personId = personId;
        this.id = id;
        this.amountDue = amountDue;
        this.paymentDeadline = paymentDeadline;
    }

    public UUID getRentableId() {
        return rentableId;
    }

    public UUID getPersonId() {
        return personId;
    }

    public UUID getId() {
        return id;
    }

    public BigDecimal getAmountDue() {
        return amountDue;
    }

    public LocalDate getPaymentDeadline() {
        return paymentDeadline;
    }

    public boolean isPaid() {
        return paid;
    }

    public boolean isNotified() {
        return notified;
    }

    public void settlePayment() {
        if(!paid) {
            paid = true;
        }
        else {
            throw new IllegalStateException("This is already paid man!");
        }
    }

    public void notifyDebtor() {
        if(!notified) {
            notified = true;
            DataStock dataStock = DataStock.getInstance();
            Person debtor = dataStock.getPeople().stream()
                    .filter(person -> person.getId().equals(personId))
                    .findFirst()
                    .orElseThrow();
            debtor.alert(new TenantAlert(personId, rentableId, dataStock.getCurrentDate(), amountDue));
        }
    }
}

Answer

It seems that the answer is easy – whenever the task scheduled in ScheduledExecutorService throws an exception the task is halted and never repeated. Also the exception is not thrown visibly. The easiest way to avoid such situation is to have try-catch block in run() ,method of Runnable. Please have a look at this post: ScheduledExecutorService handling exceptions



Source: stackoverflow