Search code examples
javafinalcompletable-future

Using final variable in lambda expression Java


I have an app that fetches a lot of data, so I would like to paginate the data into chunks and process those chunks individually rather than dealing with the data all at once. So I wrote a function I am calling every n seconds to check if a chunk is done and then process that chunk. My problem is I have no way of keeping track of the fact that I just processed a chunk and that I should move onto the next chunk when it is available. I was thinking something along the lines of the code below, however I cannot call multiplier++; as it complains that it is not behaving like a final variable anymore. I would like to use something like multiplier so that once the code processes a chunk it 1) doesn't process the same chunk again and 2) moves onto the next chunk. Is it possible to do this? Is there a modifier one can put on multiplier to help avoid race conditions?

int multiplier = 1;
CompletableFuture<String> completionFuture = new CompletableFuture<>();

final ScheduledFuture<?> checkFuture = executor.scheduleAtFixedRate(() -> {
    // parse json response
    String response = getJSONResponse();
    JsonObject jsonObject = ConverterUtils.parseJson(response, true)
      .getAsJsonObject();

    int pages = jsonObject.get("stats").getAsJsonObject().get("pages").getAsInt();

    // if we have a chunk of n pages records then process them with dataHandler function
    if (pages > multiplier * bucketSize) {
        dataHandler.apply(getResponsePaginated((multiplier - 1) * bucketSize, bucketSize));
        multiplier++;
    }
    if (jsonObject.has("finishedAt") && !jsonObject.get("finishedAt").isJsonNull()) {
        // we are done!
        completionFuture.complete("");
    }

}, 0, sleep, TimeUnit.SECONDS);

Solution

  • You can use an AtomicInteger. Since this is a mutable type, you can assign it to a final variable while still being able to change its value. This also addresses the synchronization issue between the callbacks:

    final AtomicInteger multiplier = new AtomicInteger(1);
    executor.scheduleAtFixedRate(() -> {
      //...
      multiplier.incrementAndGet();
    }, 0, sleep, TimeUnit.SECONDS);