After
Example.
An enumeration class to store each product's price:
package test.albert.multithread; import lombok.AllArgsConstructor; import lombok.Getter; @AllArgsConstructor @Getter public enum ProductEnum { P01("iPhone X 64G", 35900), P02("iPhone X 256G", 41500), P03("iPhone 8 64G", 25500), P04("iPhone 8 256G", 30900), P05("iPhone 8+ 64G", 28900), P06("iPhone 8+ 256G", 34500); private String productName; private Integer amount; }
An receipt class to store calculation result:
package test.albert.multithread; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; @Data @AllArgsConstructor @NoArgsConstructor @ToString public class Receipt { private String invoiceId; private ProductEnum product; private Integer unit; private Integer amount; private String recipient; }
Callable is an interface that stands for a task that returns a result after some computations. This CalculationTask class contains our business logic, and every time a task starts, the call() method is executed.
package test.albert.multithread; import java.util.UUID; import java.util.concurrent.Callable; public class CalculationTask implements Callable<Receipt> { private ProductEnum product; private Integer unit; private String recipient; public CalculationTask(ProductEnum product, Integer unit, String recipient) { this.product = product; this.unit = unit; this.recipient = recipient; } @Override public Receipt call() throws Exception { String invoiceId = UUID.randomUUID().toString(); Integer amount = product.getAmount() * unit; return new Receipt(invoiceId, product, unit, amount, recipient); } }
This is the CalculationTaskClient where we create an Executor service with a fixed number of threads. A separate task is created for every element in a list and is submitted it to the executor. Then, we get the result of computation with the help of the returned Future object in order to operate with it as we need it.
package test.albert.multithread; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import lombok.AllArgsConstructor; import lombok.Data; import lombok.extern.slf4j.Slf4j; @Slf4j public class CalculationTaskClient { private static final Integer THREADS_COUNT = 5; private static List<Order> orders = createDummyData(); public static void main(String[] args) throws Exception { // Used to store calculation result. List<Receipt> receipts = new ArrayList<Receipt>(); // Creates a thread pool that reuses a fixed number of threads operating off a // shared unbounded queue. ExecutorService executor = Executors.newFixedThreadPool(THREADS_COUNT); // Creates multiple callable tasks List<Callable<Receipt>> callables = new ArrayList<>(); for (Order order : orders) { Callable<Receipt> task = new CalculationTask(order.getProduct(), order.getQuantity(), order.getBuyer()); callables.add(task); } try { // Executes the given tasks, returning a list of Futures holding their status // and results when all complete. List<Future<Receipt>> results = executor.invokeAll(callables); // Get execution result. for (Future<Receipt> result : results) { receipts.add(result.get()); } } catch (InterruptedException | ExecutionException e) { throw e; } executor.shutdown(); receipts.forEach(r -> log.debug(r.toString())); } private static List<Order> createDummyData() { Order o1 = new Order(ProductEnum.P01, 2, "三澄美琴"); Order o2 = new Order(ProductEnum.P02, 1, "中堂系"); Order o3 = new Order(ProductEnum.P03, 1, "久部六郎"); Order o4 = new Order(ProductEnum.P04, 4, "東海林夕子 "); Order o5 = new Order(ProductEnum.P05, 3, "三澄夏代"); Order o6 = new Order(ProductEnum.P06, 10, "神倉保夫"); Order o7 = new Order(ProductEnum.P01, 1, "糀谷夕希子"); return Arrays.asList(o1, o2, o3, o4, o5, o6, o7); } @AllArgsConstructor @Data private static class Order { private ProductEnum product; private Integer quantity; private String buyer; } }