Lombok @Singular - how to make it threadsafe?

Viewed 42

This is a question about Lombok's @Builder and @Singular annotations and thread safety

As a simplified example, consider this class:

@Builder
public class Avengers {
    @Singular
    private List<String> members;
}

This generates a builder, where I can do this:

final Avengers team = Avengers.builder()
    .member("Iron Man")
    .member("Thor")
    .member("Captain America")
    .build();
System.out.println(team.getMembers());  // ["Iron Man", "Thor", "Captain America"]

Now for the problem - what I want to do is something like this (again, this is oversimplified to make the point):

final AvengersBuilder teamBuilder = Avengers.builder();
executorService.submit(() -> teamBuilder.member("Iron Man"));
executorService.submit(() -> teamBuilder.member("Thor"));
executorService.submit(() -> teamBuilder.member("Captain America"));
// shutdown and awaitTermination would go here

final Avengers team = teamBuilder.build();

Why is this a problem?

The generated builder is not threadsafe (Lombok backs it by an ArrayList), so the result of team.getMembers() may be incomplete or in an inconsistent state due to this.

Question: Is there a way to have Lombok generate a threadsafe builder that lets me use this "Singular" pattern?

My workaround thus far has been to avoid Singular and create my own threadsafe builder methods, but I'm hoping to eliminate this boilerplate.

1 Answers

It's not possible. Anyway, trying to share a builder across multiple threads is highly unusual. You'd do much better to store all the results, then iterate them and add them all to the builder on the same thread.

List<Future<String>> futures = new ArrayList<>();  
futures.add(executorService.submit(() -> "Iron Man"));
futures.add(executorService.submit(() -> "Thor"));
futures.add(executorService.submit(() -> "Captain America"));
// shutdown and awaitTermination would go here

final AvengersBuilder teamBuilder = Avengers.builder();
for (Future<String> future : futures) {
    try {
        teamBuilder.member(future.get());
    }
    catch (InterruptedException | ExecutionException e) {
        // do something
    }
}
final Avengers team = teamBuilder.build();

This still parallelizes the generation of the items, which is presumably the slow part.

Related