PolarSPARC

Java 8 CompletableFuture :: Part 4 (Final)


Bhaskar S 11/10/2018


In Part 1, Part 2 and Part 3 of this series, we demonstrated some of the capabilities and nuances in CompletableFuture. In this final part, we will wrap-up the series by covering the following:

Let us get started without much further ado.

Case. 1

There are scenarios where one could have a task consume input from two sources of data and once the processing is completed, trigger more than one child task(s). The following diagram illustrates a simple pipeline of tasks tackling this exact case:

Pipeline
Pipeline

Note that we used the word Supply for the method thenSupply the word Combine for the method thenCombine and so on.

Listing.11
package com.polarsparc.cf.CompletableFuture;

import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class Sample11 {
    private static final Random random = new Random();
    
    // ----- Main -----
    
    public static void main(String[] args) {
        {
            ExecutorService executor = Executors.newFixedThreadPool(4);
            
            CompletableFuture cf1 = CompletableFuture.supplyAsync(() -> {
                int n = random.nextInt(1000) + 1;
                System.out.printf("[1] [%s] Random number is %d\n", Thread.currentThread().getName(), n);
                return n;
            }, executor);
            
            CompletableFuture cf2 = CompletableFuture.supplyAsync(() -> {
                int n = random.nextInt(100) + 1;
                System.out.printf("[2] [%s] Random number is %d\n", Thread.currentThread().getName(), n);
                return n;
            }, executor);
            
            CompletableFuture cf3 = cf1.thenCombineAsync(cf2, (n1, n2) -> {
                int n = n1.intValue() % n2.intValue();
                if (n <= 0) {
                    throw new RuntimeException(String.format("n1 = %d, n2 = %d => Invalid combination", 
                        n1.intValue(), n2.intValue()));
                }
                return n;
            }, executor);
            cf3.thenAcceptAsync(n -> System.out.printf("[3] [%s] Transformed number is %d\n",
                Thread.currentThread().getName(), n), executor);
            
            CompletableFuture cf4 = cf3.thenApplyAsync(n -> {
                int r = random.nextInt(5) + 1;
                System.out.printf("[4] [%s] Random seed is %d\n", Thread.currentThread().getName(), r);
                return n * r;
            }, executor);
            cf4.thenAcceptAsync(n -> System.out.printf("[5] [%s] Final number is %d\n", 
                Thread.currentThread().getName(), n), executor);
            cf4.thenRun(() -> System.out.printf("[6] [%s] Done !!!\n", Thread.currentThread().getName()));
            
            try {
                cf4.get(1000, TimeUnit.MILLISECONDS);
            } catch (Exception ex) {
                System.out.printf("EXCEPTION:: %s\n", ex.getMessage());
            }
            
            // Sleep 1 second before shutting down executor
            try {
                Thread.sleep(1000);
            } catch (Exception ex) {
                // Ignore
            }

            executor.shutdown();
        }
    }
}

Executing the program from Listing.11 will generate the following output:

Output.18

[1] [pool-1-thread-1] Random number is 47
[2] [pool-1-thread-2] Random number is 28
[4] [pool-1-thread-4] Random seed is 2
[3] [pool-1-thread-3] Transformed number is 19
[5] [pool-1-thread-2] Final number is 38
[6] [pool-1-thread-4] Done !!!

Re-running the program from Listing.11 a few times will generate the following output:

Output.19

[1] [pool-1-thread-1] Random number is 91
[2] [pool-1-thread-2] Random number is 7
EXCEPTION:: java.lang.RuntimeException: n1 = 91, n2 = 7 => Invalid combination

Again, re-running the program from Listing.11 a few times will generate the following output:

Output.20

[1] [pool-1-thread-1] Random number is 818
[2] [pool-1-thread-2] Random number is 69
[4] [pool-1-thread-4] Random seed is 4
[6] [pool-1-thread-4] Done !!!
[5] [pool-1-thread-1] Final number is 236
[3] [pool-1-thread-3] Transformed number is 59

The following are some of the concepts in the context of the code in Listing.11:

Case. 2

In this example, we will measure the performance characteristics of a simple hypothetical payments processing use-case using different approaches, such as Threads, Futures, etc. For the payments processing, each payment instruction encapsulates a transaction between a buyer and a seller for a given amount. Processing each payment instruction involves the following three steps:

The following class Instruction represents a payment instruction:

Instruction
package com.polarsparc.concurrency;

import lombok.AllArgsConstructor;
import lombok.Getter;

@AllArgsConstructor
@Getter
public class Instruction {
    private String buyerId = null;
    private String sellerId = null;
    private double amount = 0.0;
}

The following class Account class represents an account (buyer or seller):

Account
package com.polarsparc.concurrency;

import lombok.AllArgsConstructor;
import lombok.Getter;

@AllArgsConstructor
@Getter
public class Account {
    private String clientId = null;
    private String accountId = null;
    private double balance = 0.0;
    
    public boolean hasBalance(double amt) {
        return balance > amt;
    }
    
    public boolean debit(double amt) {
        if (hasBalance(amt)) {
            balance -= amt;
            
            return true;
        }
        
        return false;
    }
    
    public void credit(double amt) {
        balance += amt;
    }
}

The following class Settlement wraps a payment settlement:

Settlement
package com.polarsparc.concurrency;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@NoArgsConstructor
@Getter
@Setter
public class Settlement {
    private String buyerAccount = null;
    private String sellerAccount = null;
    private double amount = 0.0;
}

The following class Utility encapsulates some utility functions:

Utility
package com.polarsparc.concurrency;

import java.util.Random;

public final class Utility {
    private static final Random RANDOM = new Random();
    
    // ----- Static Block -----
    
    static {
        RANDOM.setSeed(1000);
    }
    
    // ----- Private Constructor -----
    
    private Utility() {
    }
    
    // ----- Public Method(s) -----
    
    public static final void log(String cls, String meth, String msg) {
        System.out.printf("%d [%s] <%s:%s> %s\n", System.currentTimeMillis(), Thread.currentThread().getName(),
            cls, meth, msg);
    }
    
    public static final void delay(long ms) {
        try {
            Thread.sleep(ms);
        } catch (Exception ex) {
            // Ignore
        }
    }
    
    public static Instruction generateInstruction() {
        String bid = "B-100" + (1 + RANDOM.nextInt(5));
        String sid = "S-200" + (1 + RANDOM.nextInt(5));
        double amt = 5.00;
        
        return new Instruction(bid, sid, amt);
    }
}

The following class ClientServices encapsulates the client (buyer or seller) to account mappings:

ClientServices
package com.polarsparc.concurrency;

import java.util.HashMap;
import java.util.Map;

public final class ClientServices {
    private static final Map client2AccountTbl = new HashMap<>();
    
    // ----- Private Constructor -----
    
    private ClientServices() {
    }
    
    // ----- Public Method(s) -----
    
    public static final void setUp() {
        client2AccountTbl.put("B-1001", "A-10001");
        client2AccountTbl.put("B-1002", "A-10002");
        client2AccountTbl.put("B-1003", "A-10003");
        client2AccountTbl.put("B-1004", "A-10004");
        client2AccountTbl.put("B-1005", "A-10005");
        client2AccountTbl.put("S-2001", "A-10006");
        client2AccountTbl.put("S-2002", "A-10007");
        client2AccountTbl.put("S-2003", "A-10008");
        client2AccountTbl.put("S-2004", "A-10009");
        client2AccountTbl.put("S-2005", "A-10010");
    }
    
    public static final String lookupAccountByClientId(String id) {
        Utility.delay(150);
        
        if (id != null && id.isBlank() == false) {
            String acctId = client2AccountTbl.get(id);
            
//            String msg = String.format("Given client id - %s, Mapped account id - %s", id, acctId);
//            Utility.log("ClientServices", "lookupAccountByClientId", msg);
            
            return acctId;
        }
        
        return null;
    }
}

The following class AccountServices encapsulates payment processing by providing a simple facade method for debit and credit operation:

AccountServices
package com.polarsparc.concurrency;

import java.util.HashMap;
import java.util.Map;

public final class AccountServices {
    private static final Map clientAccounts = new HashMap<>();
    
    // ----- Private Constructor -----
            
    private AccountServices() {
    }
    
    // ----- Public Method(s) -----
    
    public static final void setUp() {
        clientAccounts.put("A-10001", new Account("B-1001", "A-10001", 10000.00));
        clientAccounts.put("A-10002", new Account("B-1002", "A-10002", 12500.00));
        clientAccounts.put("A-10003", new Account("B-1003", "A-10003", 5000.00));
        clientAccounts.put("A-10004", new Account("B-1004", "A-10004", 15000.00));
        clientAccounts.put("A-10005", new Account("B-1005", "A-10005", 7500.00));
        clientAccounts.put("A-10006", new Account("S-2001", "A-10006", 10000.00));
        clientAccounts.put("A-10007", new Account("S-2002", "A-10007", 5000.00));
        clientAccounts.put("A-10008", new Account("S-2003", "A-10008", 15000.00));
        clientAccounts.put("A-10009", new Account("S-2004", "A-10009", 7500.00));
        clientAccounts.put("A-10010", new Account("S-2005", "A-10010", 10000.00));
    }
    
    public static final void settle(String bid, String sid, double amt) throws Exception {
        Utility.delay(350);
        
        if (bid != null && bid.isBlank() == false && sid != null && sid.isBlank() == false) {
            synchronized(clientAccounts) {
                Account bacct = clientAccounts.get(bid);
                Account sacct = clientAccounts.get(sid);
                if (bacct != null && sacct != null) {
                    if (bacct.debit(amt)) {
                        sacct.credit(amt);
                    } else {
                        throw new Exception(String.format("Exception: Buyer account id - %s, Specified amount - %.2f,"
                            + "Balance - %.2f",    bid, amt, bacct.getBalance()));
                    }
                }
                
                String msg = String.format("Buyer account - %s, Seller account - %s, Amount - %.2f, "
                    + "Buyer balance - %.2f, Seller balance - %.2f", bid, sid, amt, bacct.getBalance(), sacct.getBalance());
                Utility.log("AccountServices", "settle", msg);
            }
        }
    }
}

And finally, the following class PaymentsProcessor is the driver for measuring the performance characteristics of the various approaches to payments processing:

PaymentsProcessor
package com.polarsparc.concurrency;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public final class PaymentsProcessor {
    private static final int COUNT = 10;
    private static final int POOLSZ = 4;
    private static final List<Instruction> INSTRUCTIONS = new ArrayList<>(10);
    
    // ----- Static Block -----
    
    static {
        for (int i = 1; i <= COUNT; i++) {
            INSTRUCTIONS.add(Utility.generateInstruction());
        }
    }
    
    // ----- Main -----
    
    /*
     * Steps for a payment on an instruction from a buyer to a seller:
     * 
     * 1. Lookup the account of the buyer
     * 2. Lookup the account of the seller
     * 3. Settle the payment - credit the seller and debit the buyer
     */
    
    public static void main(String[] args) {
        Utility.log("PaymentsProcessor", "main", "---> Ready to start");
        
        approachOne();
        approachTwo();
        approachThree();
        approachFour();
        approachFive();
        
        Utility.log("PaymentsProcessor", "main", "---> Done");
    }
    
    // ----- Method(s) -----
    
    // Sequential processing
    public static void approachOne() {
        ClientServices.setUp();
        AccountServices.setUp();
        
        Utility.log("PaymentsProcessor", "approachOne", "=====> Ready to start");
        
        long start = System.nanoTime();
        
        for (Instruction inst : INSTRUCTIONS) {
            String bacct = ClientServices.lookupAccountByClientId(inst.getBuyerId());
            String sacct = ClientServices.lookupAccountByClientId(inst.getSellerId());
            try {
                AccountServices.settle(bacct, sacct, inst.getAmount());
            } catch (Exception ex) {
                Utility.log("PaymentsProcessor", "approachOne", ex.getMessage());
            }
        }
        
        long end = System.nanoTime();
        
        String msg = String.format("=====> Average time - %d ms", (TimeUnit.NANOSECONDS.toMillis(end-start)/COUNT));
        Utility.log("PaymentsProcessor", "approachOne", msg);
    }
    
    // Using java threads
    public static void approachTwo() {
        ClientServices.setUp();
        AccountServices.setUp();
        
        Utility.log("PaymentsProcessor", "approachTwo", "=====> Ready to start");
        
        long start = System.nanoTime();
        
        for (Instruction inst : INSTRUCTIONS) {
            Settlement smt = new Settlement();
            smt.setAmount(inst.getAmount());
            
            Thread thr1 = new Thread(() -> {
                smt.setBuyerAccount(ClientServices.lookupAccountByClientId(inst.getBuyerId()));
            });
            thr1.setName("thread-1");
            thr1.start();
            
            Thread thr2 = new Thread(() -> {
                smt.setSellerAccount(ClientServices.lookupAccountByClientId(inst.getSellerId()));
            });
            thr2.setName("thread-2");
            thr2.start();
            
            try {
                thr1.join();
                thr2.join();
            } catch (Exception ex) {
                Utility.log("PaymentsProcessor", "approachTwo", ex.getMessage());
            }
            
            Thread thr3 = new Thread(() -> {
                try {
                    AccountServices.settle(smt.getBuyerAccount(), smt.getSellerAccount(), smt.getAmount());
                } catch (Exception ex) {
                    Utility.log("PaymentsProcessor", "approachTwo", ex.getMessage());
                }
            });
            thr3.setName("thread-3");
            thr3.start();
            
            try {
                thr3.join();
            } catch (Exception ex) {
                Utility.log("PaymentsProcessor", "approachTwo", ex.getMessage());
            }
        }
        
        long end = System.nanoTime();
        
        String msg = String.format("=====> Average time - %d ms", (TimeUnit.NANOSECONDS.toMillis(end-start)/COUNT));
        Utility.log("PaymentsProcessor", "approachTwo", msg);
    }
    
    // Using java futures
    public static void approachThree() {
        ClientServices.setUp();
        AccountServices.setUp();
        
        ExecutorService executor = Executors.newFixedThreadPool(POOLSZ);
        
        Utility.log("PaymentsProcessor", "approachThree", "=====> Ready to start");
        
        long start = System.nanoTime();
        
        for (Instruction inst : INSTRUCTIONS) {
            Settlement smt = new Settlement();
            smt.setAmount(inst.getAmount());
            
            Future<String> fb = executor.submit(() -> {
                return ClientServices.lookupAccountByClientId(inst.getBuyerId());
            });
            
            Future<String> fs = executor.submit(() -> {
                return ClientServices.lookupAccountByClientId(inst.getSellerId());
            });
            
            try {
                smt.setBuyerAccount(fb.get());
                smt.setSellerAccount(fs.get());
            } catch (Exception ex) {
                Utility.log("PaymentsProcessor", "approachThree", ex.getMessage());
            }
            
            Future fdc = executor.submit(() -> {
                try {
                    AccountServices.settle(smt.getBuyerAccount(), smt.getSellerAccount(), smt.getAmount());
                } catch (Exception ex) {
                    Utility.log("PaymentsProcessor", "approachThree", ex.getMessage());
                }
            });
            
            try {
                fdc.get();
            } catch (Exception ex) {
                Utility.log("PaymentsProcessor", "approachThree", ex.getMessage());
            }
        }
        
        long end = System.nanoTime();
        
        executor.shutdown();
        
        String msg = String.format("=====> Average time - %d ms", (TimeUnit.NANOSECONDS.toMillis(end-start)/COUNT));
        Utility.log("PaymentsProcessor", "approachThree", msg);
    }
    
    
    // Using java futures
    public static void approachFour() {
        ClientServices.setUp();
        AccountServices.setUp();
        
        ExecutorService executor = Executors.newFixedThreadPool(POOLSZ);
        
        List<Future<?>> futures = new ArrayList<>();
        
        Utility.log("PaymentsProcessor", "approachFour", "=====> Ready to start");
        
        long start = System.nanoTime();
        
        for (Instruction inst : INSTRUCTIONS) {
            Future fb = executor.submit(() -> {
                Settlement smt = new Settlement();
                smt.setAmount(inst.getAmount());
                
                smt.setBuyerAccount(ClientServices.lookupAccountByClientId(inst.getBuyerId()));
                smt.setSellerAccount(ClientServices.lookupAccountByClientId(inst.getSellerId()));
                
                try {
                    AccountServices.settle(smt.getBuyerAccount(), smt.getSellerAccount(), smt.getAmount());
                } catch (Exception ex) {
                    Utility.log("PaymentsProcessor", "approachFour", ex.getMessage());
                }
            });
            
            futures.add(fb);
        }
        
        for (Future fut : futures) {
            try {
                fut.get();
            } catch (Exception ex) {
                Utility.log("PaymentsProcessor", "approachFour", ex.getMessage());
            }
        }
        
        long end = System.nanoTime();
        
        executor.shutdown();
        
        String msg = String.format("=====> Average time - %d ms", (TimeUnit.NANOSECONDS.toMillis(end-start)/COUNT));
        Utility.log("PaymentsProcessor", "approachFour", msg);
    }
    
    // Using java completable futures
    public static void approachFive() {
        ClientServices.setUp();
        AccountServices.setUp();
        
        ExecutorService executor = Executors.newFixedThreadPool(POOLSZ);
        
        List<CompletableFuture<?>> cfList = new ArrayList<>();
        
        Utility.log("PaymentsProcessor", "approachFive", "=====> Ready to start");
        
        long start = System.nanoTime();
        
        for (Instruction inst : INSTRUCTIONS) {
            CompletableFuture<String> bcf = CompletableFuture.supplyAsync(() -> 
                ClientServices.lookupAccountByClientId(inst.getBuyerId()), executor);
            
            CompletableFuture<String> scf = CompletableFuture.supplyAsync(() -> 
                ClientServices.lookupAccountByClientId(inst.getSellerId()), executor);
            
            CompletableFuture<Settlement> bscf = bcf.thenCombineAsync(scf, (ba, sa) -> {
                Settlement smt = new Settlement();
                smt.setBuyerAccount(ba);
                smt.setSellerAccount(sa);
                smt.setAmount(inst.getAmount());
                return smt;
            });
            
            CompletableFuture<Void> fcf = bscf.thenAcceptAsync(smt -> {
                try {
                    AccountServices.settle(smt.getBuyerAccount(), smt.getSellerAccount(), smt.getAmount());
                } catch (Exception ex) {
                    Utility.log("PaymentsProcessor", "approachFive", ex.getMessage());
                }
            });
            
            cfList.add(fcf);
        }
        
        for (CompletableFuture<?> cf : cfList) {
            try {
                cf.get();
            } catch (Exception ex) {
                Utility.log("PaymentsProcessor", "approachFive", ex.getMessage());
            }
        }
        
        long end = System.nanoTime();
        
        executor.shutdown();
        
        String msg = String.format("=====> Average time - %d ms", (TimeUnit.NANOSECONDS.toMillis(end-start)/COUNT));
        Utility.log("PaymentsProcessor", "approachFive", msg);
    }
}

Executing the above program PaymentsProcessor will generate the following output:

Output.21

1541882058692 [main] <PaymentsProcessor:main> ---> Ready to start
1541882058719 [main] <PaymentsProcessor:approachOne> =====> Ready to start
1541882059378 [main] <AccountServices:settle> Buyer account - A-10003, Seller account - A-10006, Amount - 5.00, Buyer balance - 4995.00, Seller balance - 10005.00
1541882060031 [main] <AccountServices:settle> Buyer account - A-10002, Seller account - A-10010, Amount - 5.00, Buyer balance - 12495.00, Seller balance - 10005.00
1541882060685 [main] <AccountServices:settle> Buyer account - A-10003, Seller account - A-10010, Amount - 5.00, Buyer balance - 4990.00, Seller balance - 10010.00
1541882061338 [main] <AccountServices:settle> Buyer account - A-10002, Seller account - A-10006, Amount - 5.00, Buyer balance - 12490.00, Seller balance - 10010.00
1541882061991 [main] <AccountServices:settle> Buyer account - A-10005, Seller account - A-10006, Amount - 5.00, Buyer balance - 7495.00, Seller balance - 10015.00
1541882062645 [main] <AccountServices:settle> Buyer account - A-10005, Seller account - A-10010, Amount - 5.00, Buyer balance - 7490.00, Seller balance - 10015.00
1541882063298 [main] <AccountServices:settle> Buyer account - A-10003, Seller account - A-10009, Amount - 5.00, Buyer balance - 4985.00, Seller balance - 7505.00
1541882063951 [main] <AccountServices:settle> Buyer account - A-10002, Seller account - A-10006, Amount - 5.00, Buyer balance - 12485.00, Seller balance - 10020.00
1541882064604 [main] <AccountServices:settle> Buyer account - A-10002, Seller account - A-10008, Amount - 5.00, Buyer balance - 12480.00, Seller balance - 15005.00
1541882065257 [main] <AccountServices:settle> Buyer account - A-10004, Seller account - A-10007, Amount - 5.00, Buyer balance - 14995.00, Seller balance - 5005.00
1541882065259 [main] <PaymentsProcessor:approachOne> =====> Average time - 653 ms
1541882065261 [main] <PaymentsProcessor:approachTwo> =====> Ready to start
1541882065770 [thread-3] <AccountServices:settle> Buyer account - A-10003, Seller account - A-10006, Amount - 5.00, Buyer balance - 4995.00, Seller balance - 10005.00
1541882066274 [thread-3] <AccountServices:settle> Buyer account - A-10002, Seller account - A-10010, Amount - 5.00, Buyer balance - 12495.00, Seller balance - 10005.00
1541882066778 [thread-3] <AccountServices:settle> Buyer account - A-10003, Seller account - A-10010, Amount - 5.00, Buyer balance - 4990.00, Seller balance - 10010.00
1541882067281 [thread-3] <AccountServices:settle> Buyer account - A-10002, Seller account - A-10006, Amount - 5.00, Buyer balance - 12490.00, Seller balance - 10010.00
1541882067784 [thread-3] <AccountServices:settle> Buyer account - A-10005, Seller account - A-10006, Amount - 5.00, Buyer balance - 7495.00, Seller balance - 10015.00
1541882068288 [thread-3] <AccountServices:settle> Buyer account - A-10005, Seller account - A-10010, Amount - 5.00, Buyer balance - 7490.00, Seller balance - 10015.00
1541882068791 [thread-3] <AccountServices:settle> Buyer account - A-10003, Seller account - A-10009, Amount - 5.00, Buyer balance - 4985.00, Seller balance - 7505.00
1541882069295 [thread-3] <AccountServices:settle> Buyer account - A-10002, Seller account - A-10006, Amount - 5.00, Buyer balance - 12485.00, Seller balance - 10020.00
1541882069798 [thread-3] <AccountServices:settle> Buyer account - A-10002, Seller account - A-10008, Amount - 5.00, Buyer balance - 12480.00, Seller balance - 15005.00
1541882070301 [thread-3] <AccountServices:settle> Buyer account - A-10004, Seller account - A-10007, Amount - 5.00, Buyer balance - 14995.00, Seller balance - 5005.00
1541882070303 [main] <PaymentsProcessor:approachTwo> =====> Average time - 504 ms
1541882070313 [main] <PaymentsProcessor:approachThree> =====> Ready to start
1541882070821 [pool-1-thread-3] <AccountServices:settle> Buyer account - A-10003, Seller account - A-10006, Amount - 5.00, Buyer balance - 4995.00, Seller balance - 10005.00
1541882071324 [pool-1-thread-1] <AccountServices:settle> Buyer account - A-10002, Seller account - A-10010, Amount - 5.00, Buyer balance - 12495.00, Seller balance - 10005.00
1541882071827 [pool-1-thread-2] <AccountServices:settle> Buyer account - A-10003, Seller account - A-10010, Amount - 5.00, Buyer balance - 4990.00, Seller balance - 10010.00
1541882072330 [pool-1-thread-3] <AccountServices:settle> Buyer account - A-10002, Seller account - A-10006, Amount - 5.00, Buyer balance - 12490.00, Seller balance - 10010.00
1541882072832 [pool-1-thread-4] <AccountServices:settle> Buyer account - A-10005, Seller account - A-10006, Amount - 5.00, Buyer balance - 7495.00, Seller balance - 10015.00
1541882073335 [pool-1-thread-1] <AccountServices:settle> Buyer account - A-10005, Seller account - A-10010, Amount - 5.00, Buyer balance - 7490.00, Seller balance - 10015.00
1541882073837 [pool-1-thread-2] <AccountServices:settle> Buyer account - A-10003, Seller account - A-10009, Amount - 5.00, Buyer balance - 4985.00, Seller balance - 7505.00
1541882074339 [pool-1-thread-3] <AccountServices:settle> Buyer account - A-10002, Seller account - A-10006, Amount - 5.00, Buyer balance - 12485.00, Seller balance - 10020.00
1541882074842 [pool-1-thread-4] <AccountServices:settle> Buyer account - A-10002, Seller account - A-10008, Amount - 5.00, Buyer balance - 12480.00, Seller balance - 15005.00
1541882075344 [pool-1-thread-2] <AccountServices:settle> Buyer account - A-10004, Seller account - A-10007, Amount - 5.00, Buyer balance - 14995.00, Seller balance - 5005.00
1541882075346 [main] <PaymentsProcessor:approachThree> =====> Average time - 503 ms
1541882075348 [main] <PaymentsProcessor:approachFour> =====> Ready to start
1541882076002 [pool-2-thread-1] <AccountServices:settle> Buyer account - A-10003, Seller account - A-10006, Amount - 5.00, Buyer balance - 4995.00, Seller balance - 10005.00
1541882076004 [pool-2-thread-4] <AccountServices:settle> Buyer account - A-10002, Seller account - A-10006, Amount - 5.00, Buyer balance - 12495.00, Seller balance - 10010.00
1541882076005 [pool-2-thread-3] <AccountServices:settle> Buyer account - A-10003, Seller account - A-10010, Amount - 5.00, Buyer balance - 4990.00, Seller balance - 10005.00
1541882076006 [pool-2-thread-2] <AccountServices:settle> Buyer account - A-10002, Seller account - A-10010, Amount - 5.00, Buyer balance - 12490.00, Seller balance - 10010.00
1541882076654 [pool-2-thread-1] <AccountServices:settle> Buyer account - A-10005, Seller account - A-10006, Amount - 5.00, Buyer balance - 7495.00, Seller balance - 10015.00
1541882076656 [pool-2-thread-4] <AccountServices:settle> Buyer account - A-10005, Seller account - A-10010, Amount - 5.00, Buyer balance - 7490.00, Seller balance - 10015.00
1541882076657 [pool-2-thread-3] <AccountServices:settle> Buyer account - A-10003, Seller account - A-10009, Amount - 5.00, Buyer balance - 4985.00, Seller balance - 7505.00
1541882076659 [pool-2-thread-2] <AccountServices:settle> Buyer account - A-10002, Seller account - A-10006, Amount - 5.00, Buyer balance - 12485.00, Seller balance - 10020.00
1541882077306 [pool-2-thread-1] <AccountServices:settle> Buyer account - A-10002, Seller account - A-10008, Amount - 5.00, Buyer balance - 12480.00, Seller balance - 15005.00
1541882077308 [pool-2-thread-4] <AccountServices:settle> Buyer account - A-10004, Seller account - A-10007, Amount - 5.00, Buyer balance - 14995.00, Seller balance - 5005.00
1541882077309 [main] <PaymentsProcessor:approachFour> =====> Average time - 195 ms
1541882077310 [main] <PaymentsProcessor:approachFive> =====> Ready to start
1541882077827 [ForkJoinPool.commonPool-worker-7] <AccountServices:settle> Buyer account - A-10003, Seller account - A-10006, Amount - 5.00, Buyer balance - 4995.00, Seller balance - 10005.00
1541882077829 [ForkJoinPool.commonPool-worker-5] <AccountServices:settle> Buyer account - A-10002, Seller account - A-10010, Amount - 5.00, Buyer balance - 12495.00, Seller balance - 10005.00
1541882077975 [ForkJoinPool.commonPool-worker-9] <AccountServices:settle> Buyer account - A-10003, Seller account - A-10010, Amount - 5.00, Buyer balance - 4990.00, Seller balance - 10010.00
1541882077976 [ForkJoinPool.commonPool-worker-3] <AccountServices:settle> Buyer account - A-10002, Seller account - A-10006, Amount - 5.00, Buyer balance - 12490.00, Seller balance - 10010.00
1541882078126 [ForkJoinPool.commonPool-worker-11] <AccountServices:settle> Buyer account - A-10005, Seller account - A-10006, Amount - 5.00, Buyer balance - 7495.00, Seller balance - 10015.00
1541882078127 [ForkJoinPool.commonPool-worker-13] <AccountServices:settle> Buyer account - A-10005, Seller account - A-10010, Amount - 5.00, Buyer balance - 7490.00, Seller balance - 10015.00
1541882078276 [ForkJoinPool.commonPool-worker-5] <AccountServices:settle> Buyer account - A-10003, Seller account - A-10009, Amount - 5.00, Buyer balance - 4985.00, Seller balance - 7505.00
1541882078277 [ForkJoinPool.commonPool-worker-7] <AccountServices:settle> Buyer account - A-10002, Seller account - A-10006, Amount - 5.00, Buyer balance - 12485.00, Seller balance - 10020.00
1541882078426 [ForkJoinPool.commonPool-worker-3] <AccountServices:settle> Buyer account - A-10002, Seller account - A-10008, Amount - 5.00, Buyer balance - 12480.00, Seller balance - 15005.00
1541882078427 [ForkJoinPool.commonPool-worker-9] <AccountServices:settle> Buyer account - A-10004, Seller account - A-10007, Amount - 5.00, Buyer balance - 14995.00, Seller balance - 5005.00
1541882078429 [main] <PaymentsProcessor:approachFive> =====> Average time - 111 ms
1541882078430 [main] <PaymentsProcessor:main> ---> Done

Notice from the listing ClientServices above, we introduce a delay of 150 ms to simulate a real-world service call to look-up an account for a given client. Similarly, from the listing AccountServices above, we introduce a delay of 350 ms to simulate a real-world service call to settle a payment between two clients (buyer and seller).

The following are some of the observations from the Output.21 above:

CAUTION

One needs to realize that CompletableFuture is not a silver bullet for every situation. One needs to really understand the use-case at hand to apply the right tools for the problem.

With that we conclude this series on CompletableFuture.



© PolarSPARC