I am trying to use Java Streams to make the sequential processing of a list of customers run in parallel. This is a short-term band-aid to a problem that we are solving as part of re-architecting our entire system.
What I am starting with is a List<Customer> Customers structure that contains the customer contact information and all the relevant transaction data. Conceptually, the code I am replacing looks like:
long emailsSent = 0;
List<Customer> customers = methodLoadingAllrelevantData();
for (Customer customer: customers) {
boolean isEmailSent = sendEmail(customer);
if (isEmailSent) {
emailsSent++;
}
}
The sendMail(customer) function:
- Determines if an email should be sent
- Formats the email
- Attempts to send the email
- Returns true if the email was sent successfully
Not great code, but I am just trying to get some more speed from the existing code, not trying to make it better. The method and all its calls are 100-percent thread-safe.
I put it in the following stream structure:
ForkJoinPool limitedParallelThreadPool = new ForkJoinPool(numberOfThreads);
emailsSent = limitedParallelThreadPool.submit( () ->
customers.stream().parallel()
.map(this::_emailCustomer)
.filter(b -> b == true).count()
).get();
This does work as expected, returning the same data as the sequential version.
My questions are: because the purpose of my method is to generates an email, is it poor practice for me to use a map function? Is there a better answer? I am, in effect, mapping a Customer to a boolean, but part of this mapping requires the process to trigger an email.
I was originally trying to use a forEach()operator, but I could not figure out how to get the count without adding state information to the sendMail function, which interferes with it being thread-safe.