Java CompletableFuture allOf() anyOf() Sinhala Tutorial | Concurrency Guide

Java CompletableFuture allOf() anyOf() Sinhala Tutorial | Concurrency Guide

හලෝ හැමෝටම! Java Concurrency වල අලුත් දේවල් ඉගෙන ගන්න සුදානම්ද?

කොහොමද ඉතින්? අද අපි කතා කරන්න යන්නේ modern software development වලට ගොඩක් වැදගත් වෙන, ඒ වගේම ටිකක් සංකීර්ණ වෙන්න පුළුවන් මාතෘකාවක් ගැන. ඒ තමයි Java වල CompletableFuture භාවිතයෙන් multiple asynchronous tasks manage කරගන්න එක.

දැන් තියෙන application ගොඩක් responsive වෙන්න ඕන, ඒ වගේම එකවර tasks ගොඩක් handle කරන්නත් ඕන. මේකට අපි concurrency සහ asynchronous programming පාවිච්චි කරනවා. Java වල CompletableFuture කියන්නේ මේ වැඩේට තියෙන powerful tool එකක්. ඒත්, සමහර වෙලාවට අපිට tasks කිහිපයක් එකවර ආරම්භ කරලා, ඒ හැම task එකක්ම ඉවර වෙනකම් ඉන්න අවශ්‍ය වෙනවා, නැත්නම් ඒ tasks වලින් ඉස්සෙල්ලාම ඉවර වෙන task එකේ result එක ගන්න අවශ්‍ය වෙනවා. මේ වගේ situation වලට තමයි CompletableFuture වල allOf() සහ anyOf() කියන methods දෙක අපිට උදව් වෙන්නේ.

අද මේ tutorial එකෙන්, අපි මේ methods දෙක මොනවද, ඒවා කොහොමද වැඩ කරන්නේ, ඒ වගේම practical code examples එක්ක ඒවා කොහොමද use කරන්නේ කියලා හොඳට ඉගෙන ගමු. මේකෙන් ඔයාගේ Java applications වල performance සහ responsiveness වැඩි කරගන්න පුළුවන්!

CompletableFuture කියන්නේ මොකක්ද? Future වලට වඩා හොඳ ඇයි?

මුලින්ම, CompletableFuture කියන්නේ මොකක්ද කියලා පොඩ්ඩක් මතක් කරගමු. Java 5 වලදී හඳුන්වා දුන්න Future interface එක asynchronous calculation එකක result එකක් නියෝජනය කළා. ඒත් ඒකේ ලොකු drawback එකක් තිබුණා: get() method එක call කළාම result එක එනකම් main thread එක block වෙනවා. ඒ කියන්නේ, ඔයා network call එකක් කරලා Future.get() පාවිච්චි කරනවා නම්, result එක එනකම් ඔයාගේ application එක freeze වෙනවා වගේ දෙයක් වෙනවා.

CompletableFuture (Java 8 වලින් හඳුන්වා දුන්න) මේ ප්‍රශ්න ගොඩක් විසඳනවා. මේක Future interface එක implement කරනවා වගේම, අපිට non-blocking operations කරන්න පුළුවන් හැකියාව දෙනවා. CompletableFuture එකක් යම්කිසි task එකක් background එකේ run කරලා, ඒ task එක ඉවර වුනාට පස්සේ මොනවා හරි ක්‍රියාවක් කරන්න (e.g., thenApply(), thenAccept(), thenCompose() වගේ methods වලින්) පුළුවන් විදිහට නිර්මාණය කරලා තියෙනවා.

මේකේ ප්‍රධානම වාසිය තමයි අපිට task එකක් ඉවර වෙනකම් waiting කරන්නේ නැතුව තව tasks කරන්න පුළුවන් වීම. ඒ වගේම, error handling, chaining multiple asynchronous operations, සහ custom completion වගේ දේවල් වලටත් CompletableFuture ඉතාමත් පහසුකම් සලසනවා.

Multiple Asynchronous Tasks Manage කරන එකේ අභියෝගය

හිතන්න ඔයා online shopping application එකක් develop කරනවා කියලා. User කෙනෙක්ගේ profile එක load කරන්න ඕන වුනාම, ඔයාට tasks කිහිපයක් එකවර කරන්න වෙනවා:

  1. User ගේ personal details database එකෙන් ගන්න.
  2. User ගේ order history වෙනම microservice එකකින් fetch කරන්න.
  3. User ගේ recently viewed items තව service එකකින් ගන්න.

මේ tasks තුනම එකිනෙකට independent. ඒ කියන්නේ එකක් ඉවර වෙනකම් අනිත් එකට wait කරන්න ඕන නැහැ. අපිට පුළුවන් මේ තුනම එකවර ආරම්භ කරලා, ඊට පස්සේ හැම result එකක්ම ලැබුනට පස්සේ user ට data display කරන්න. සාමාන්‍ය Future method එකක් use කළා නම්, ඔයාට එකින් එක get() කරන්න වෙනවා, එතකොට ඒක sequential (එකින් එක) වෙන නිසා ගොඩක් වෙලා යනවා. නැත්නම්, threads කිහිපයක් manually manage කරන්න, CountDownLatch වගේ දේවල් use කරන්න වෙනවා, ඒක complex වැඩක්.

නැත්නම් හිතන්න, ඔයාට එකම data set එක different services 3කින් ගන්න පුළුවන්. ඒ services වලින් ඉස්සෙල්ලාම data දෙන service එකෙන් result එක ගන්න පුළුවන් නම්, ඔයාගේ application එකේ responsiveness එක වැඩි වෙනවා නේද? මේ වගේ scenarios වලදී තමයි CompletableFuture.allOf() සහ CompletableFuture.anyOf() අපිට කදිම විසඳුම් දෙන්නේ.

CompletableFuture.allOf() - හැමෝම ඉවර වෙනකම් ඉමු!

CompletableFuture.allOf() method එක පාවිච්චි කරන්නේ, ඔයාට asynchronous tasks කිහිපයක් ආරම්භ කරලා, ඒ tasks හැමෝම සාර්ථකව complete වෙනකම් ඉන්න අවශ්‍ය වුනාමයි. මේ method එකට CompletableFuture objects කීපයක් arguments විදිහට දීලා, ඒ හැම future එකක්ම complete වුනාම complete වෙන CompletableFuture<Void> එකක් return කරනවා.

මතක තියාගන්න: allOf() return කරන්නේ CompletableFuture<Void> එකක්. ඒ කියන්නේ මේ future එකට තනි result එකක් නැහැ. ඒකට හේතුව තමයි, ඔයා allOf() ට දෙන future objects වලට (උදාහරණයක් විදිහට CompletableFuture<String>, CompletableFuture<Integer> වගේ) විවිධ data types තියෙන්න පුළුවන් නිසා, ඒ හැම එකක්ම combine කරලා common type එකක් හදන්න බැරි වීම. ඉතින්, හැම future එකක්ම complete වුනාට පස්සේ, ඔයාට ඒ individual future objects වලින් ඒවායේ results එකින් එක get() හෝ join() කරලා ගන්න වෙනවා.

Error Handling: allOf() එකකට දීලා තියෙන future objects වලින් එකක් හෝ error එකක් එක්ක complete වුනොත්, allOf() future එකත් exception එකක් එක්ක complete වෙනවා. ඒක හරියට "All or Nothing" වගේ දෙයක්.

allOf() භාවිතයේ practical example එකක්

අපි හිතමු අපිට user details සහ orders details වෙන වෙනම services වලින් fetch කරන්න ඕන කියලා. මේ tasks දෙක එකවර run කරලා, දෙකේම results ලැබුනට පස්සේ ඒ දෙකම combine කරලා display කරමු.

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.List;
import java.util.Arrays;
import java.util.stream.Collectors;

public class AllOfExample {
    public static void main(String[] args) throws Exception {

        System.out.println("User details fetch කරනවා...");
        // User details fetch කරන්න asynchronous task එකක්
        CompletableFuture<String> userDetailsFuture = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(2); // Network delay එකක් simulate කරමු
                System.out.println("User Details task completed.");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            return "User: John Doe, Email: [email protected]";
        });

        System.out.println("Order history fetch කරනවා...");
        // Order history fetch කරන්න asynchronous task එකක්
        CompletableFuture<List<String>> orderHistoryFuture = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(3); // ටිකක් වැඩි delay එකක් simulate කරමු
                System.out.println("Order History task completed.");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            return Arrays.asList("Laptop", "Mouse", "Keyboard");
        });

        // මේ future objects දෙකම complete වෙනකම් ඉන්න allOf() පාවිච්චි කරමු
        // මේකෙන් ලැබෙන්නේ CompletableFuture<Void> එකක්
        CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(userDetailsFuture, orderHistoryFuture);

        // combinedFuture එක complete වෙනකම් block කරනවා (practical applications වලට non-blocking ක්‍රම recommend කරනවා)
        // join() method එක get() වගේම වැඩ, නමුත් checked exceptions throw කරන්නේ නැහැ.
        combinedFuture.join();

        System.out.println("\nහැම future එකක්ම සාර්ථකව complete වුනා! දැන් results ගන්න පුළුවන්.");

        // Individual future objects වලින් results ගන්න.
        // real world වල exception handling කරන්න ඕනේ.
        String userDetails = userDetailsFuture.get();
        List<String> orderHistory = orderHistoryFuture.get();

        System.out.println("User Details: " + userDetails);
        System.out.println("Order History: " + orderHistory);

        System.out.println("\n--- Results Combine කරන තව ලස්සන විදිහක් ---");
        // results combine කරලා එකම String එකක් විදිහට ගන්නත් පුළුවන්
        String finalCombinedResult = CompletableFuture.allOf(userDetailsFuture, orderHistoryFuture)
                .thenApply(v -> {
                    // allOf() එක complete වුනාට පස්සේ, individual futures වලින් results join කරන්න පුළුවන්.
                    // මේ join() methods block වෙන්නේ නැහැ, මොකද අදාල future එක දැනටමත් complete නිසා.
                    String details = userDetailsFuture.join();
                    List<String> orders = orderHistoryFuture.join();
                    return "සම්පූර්ණ තොරතුරු: " + details + ", ඇණවුම්: " + orders.stream().collect(Collectors.joining(", "));
                }).join(); // අවසාන result එක ගන්න join() call කරනවා.

        System.out.println(finalCombinedResult);
    }
}

මේ example එකේදී, userDetailsFuture එක තත්පර 2ක් වගේ වෙලාවකින් complete වෙනවා, orderHistoryFuture එක තත්පර 3කින් complete වෙනවා. allOf() එකෙන් ලැබෙන combinedFuture එක complete වෙන්නේ වෙලා වැඩිම future එක (තත්පර 3ක orderHistoryFuture) complete වුනාම. ඊට පස්සේ අපිට individual futures වලින් result ගන්න පුළුවන්.

CompletableFuture.anyOf() - කවුරු හරි ඉස්සෙල්ලා ඉවර වෙනකම් ඉමු!

CompletableFuture.anyOf() method එක භාවිතා කරන්නේ, ඔයාට asynchronous tasks කිහිපයක් ආරම්භ කරලා, ඒ tasks වලින් ඕනෑම එකක් සාර්ථකව complete වෙනකම් විතරක් ඉන්න අවශ්‍ය වුනාමයි. මේ method එකට CompletableFuture objects කීපයක් arguments විදිහට දීලා, ඒ tasks වලින් ඉස්සෙල්ලාම complete වෙන task එකේ result එක එක්ක complete වෙන CompletableFuture<Object> එකක් return කරනවා.

මතක තියාගන්න: anyOf() return කරන්නේ CompletableFuture<Object> එකක්. ඒකට හේතුවත් allOf() වගේමයි – ඉස්සෙල්ලාම complete වෙන future එක මොන type එකේ එකක්ද කියලා අපිට කලින්ම කියන්න බැහැ. ඉතින්, result එකක් ලැබුනට පස්සේ, ඔයාට ඒක අදාල data type එකට cast කරගන්න සිද්ධ වෙනවා.

Use Cases: anyOf() ගොඩක් වෙලාවට use කරන්නේ redundancy තියෙන වෙලාවට. උදාහරණයක් විදිහට, ඔයාට එකම data set එක different servers 3කින් fetch කරන්න පුළුවන්. Fastest server එකෙන් data එක ගන්න anyOf() පාවිච්චි කරන්න පුළුවන්. නැත්නම්, different algorithms කිහිපයක් එකම problem එක solve කරන්න යොදවලා, fastest solution එක ගන්නත් පුළුවන්.

Error Handling: anyOf() එකකට දීලා තියෙන future objects වලින් ඉස්සෙල්ලාම complete වෙන future එක exception එකක් එක්ක complete වුනොත්, anyOf() future එකත් exception එකක් එක්ක complete වෙනවා. ඒත්, අනිත් futures සාර්ථකව complete වෙන්න පුළුවන්. හැබැයි, ඉස්සෙල්ලාම exception එකක් ආවොත්, ඒක තමයි ඔයාගේ anyOf() එකෙන් report වෙන්නේ.

anyOf() භාවිතයේ practical example එකක්

අපි හිතමු අපිට එකම product details එක servers තුනකින් fetch කරන්න පුළුවන් කියලා. අපිට ඕන මේ servers තුනෙන් ඉස්සෙල්ලාම response එක දෙන server එකේ result එක ගන්න.

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

public class AnyOfExample {
    public static void main(String[] args) throws Exception {

        System.out.println("Product details servers තුනකින් fetch කරනවා...");

        // Server A: slow response
        CompletableFuture<String> serverA = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(4); // තත්පර 4ක delay එකක්
                System.out.println("Server A completed.");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            return "Product from Server A: High-end Gaming PC";
        });

        // Server B: fastest response
        CompletableFuture<String> serverB = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(1); // තත්පර 1ක delay එකක් (fastest)
                System.out.println("Server B completed.");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            return "Product from Server B: Mechanical Keyboard";
        });

        // Server C: medium response
        CompletableFuture<String> serverC = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(2); // තත්පර 2ක delay එකක්
                System.out.println("Server C completed.");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            return "Product from Server C: Wireless Mouse";
        });

        // මේ future objects තුනෙන් ඕනෑම එකක් complete වෙනකම් ඉන්න anyOf() පාවිච්චි කරමු
        // මේකෙන් ලැබෙන්නේ CompletableFuture<Object> එකක්
        CompletableFuture<Object> combinedFuture = CompletableFuture.anyOf(serverA, serverB, serverC);

        // ඉස්සෙල්ලාම complete වෙන future එකේ result එක ගන්න.
        // join() method එක block කරනවා ඉස්සෙල්ලාම complete වෙනකන්.
        Object result = combinedFuture.join();

        System.out.println("\nඉස්සෙල්ලාම complete වුනු server එකේ Result: " + result);

        // Result එක මොකක්ද කියලා දැනගන්න ඕන නම්, type check කරගන්න පුළුවන්
        if (result instanceof String) {
            String productResult = (String) result;
            if (productResult.contains("Server B")) {
                System.out.println("Server B තමයි ඉස්සෙල්ලාම response එක දුන්නේ!");
            }
        }
    }
}

මේ example එකේදී, serverB එක තත්පර 1කින් complete වෙන නිසා, anyOf() එකෙන් ලැබෙන combinedFuture එක තත්පර 1කින් complete වෙනවා. අනිත් servers වලට තව වෙලාවක් ගියත්, ඒ results අපිට අදාල නැහැ, මොකද අපිට අවශ්‍ය වුනේ fastest response එක විතරයි.

අවසාන වශයෙන්

ඉතින් යාලුවනේ, අද අපි Java වල CompletableFuture එකේ allOf() සහ anyOf() කියන powerful methods දෙක ගැන ඉගෙන ගත්තා. මේවා අපිට complicated asynchronous workflows හසුරුවන්න, application එකේ responsiveness එක වැඩි කරන්න, ඒ වගේම code එක වඩාත් කියවන්න පහසු සහ maintain කරන්න පහසු විදිහට ලියන්න ලොකු support එකක් දෙනවා.

  • allOf(): එකවර tasks කිහිපයක් ආරම්භ කරලා, හැම task එකක්ම complete වෙනකම් ඉන්න අවශ්‍ය වුනාම use කරනවා. හැම future එකක්ම සාර්ථක වුනොත් විතරයි මේක සාර්ථක වෙන්නේ. Result එක Void නිසා, individual futures වලින් result ගන්න වෙනවා.
  • anyOf(): එකවර tasks කිහිපයක් ආරම්භ කරලා, ඒ tasks වලින් ඉස්සෙල්ලාම complete වෙන task එකේ result එක ගන්න අවශ්‍ය වුනාම use කරනවා. Result එක Object නිසා, cast කරගන්න වෙනවා.

මේ concepts ඔයාගේ next Java project එකට implement කරලා බලන්න. Concurrency කියන්නේ මුලින්ම ටිකක් අමාරු වුනත්, practice කරනකොට හොඳට තේරෙන දෙයක්. මේවා හරියට use කළොත් ඔයාට scalable, efficient applications හදන්න පුළුවන්.

ඔයාට මොනවා හරි ප්‍රශ්න තියෙනවා නම්, මේ ගැන ඔයාගේ අත්දැකීම් මොනවද කියලා පහලින් comment එකක් දාන්න. අපි කතා කරමු! Thank you!