Əsas məzmuna keçin

Java Future və CompletableFuture

Future Nədir?

Future asynchronous əməliyyatların nəticəsini təmsil edir. Əməliyyat tamamlanmamış ola bilər və nəticə gələcəkdə əldə ediləcək.

Future Interface

Basic Usage

ExecutorService executor = Executors.newFixedThreadPool(2);

Future<String> future = executor.submit(() -> {
Thread.sleep(2000);
return "Hello from Future!";
});

// Nəticəni əldə et (blocking)
String result = future.get(); // 2 saniyə gözləyəcək
System.out.println(result);

executor.shutdown();

Future Metodları

Future<Integer> future = executor.submit(() -> {
Thread.sleep(3000);
return 42;
});

// Status yoxlama
boolean isDone = future.isDone(); // false (əvvəlcə)
boolean isCancelled = future.isCancelled(); // false

// Timeout ilə
try {
Integer result = future.get(1, TimeUnit.SECONDS); // TimeoutException
} catch (TimeoutException e) {
System.out.println("Timeout!");
}

// Cancel etmək
boolean cancelled = future.cancel(true); // true = interrupt if running

CompletableFuture

Java 8-də təqdim edilmiş, daha güclü Future implementasiyası.

Simple Usage

// Async task yaratmaq
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
return "Error";
}
return "Hello CompletableFuture!";
});

// Nəticəni işləmək (non-blocking)
future.thenAccept(System.out::println);

// Blocking get
String result = future.get();

Chaining Operations

thenApply() - Transform

CompletableFuture<Integer> future = CompletableFuture
.supplyAsync(() -> "42")
.thenApply(Integer::parseInt)
.thenApply(n -> n * 2);

future.thenAccept(System.out::println); // 84

thenCombine() - Combine Two Futures

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");

CompletableFuture<String> combined = future1.thenCombine(future2,
(s1, s2) -> s1 + " " + s2);

combined.thenAccept(System.out::println); // Hello World

Error Handling

handle() - Handle Both Result and Exception

CompletableFuture<String> future = CompletableFuture
.supplyAsync(() -> {
if (Math.random() > 0.5) {
throw new RuntimeException("Random error!");
}
return "Success";
})
.handle((result, ex) -> {
if (ex != null) {
return "Error: " + ex.getMessage();
}
return result;
});

exceptionally() - Handle Only Exceptions

CompletableFuture<String> future = CompletableFuture
.supplyAsync(() -> {
throw new RuntimeException("Error occurred");
})
.exceptionally(ex -> "Default value");

future.thenAccept(System.out::println); // Default value

Combining Multiple Futures

allOf() - Wait for All

CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> "Task 1");
CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> "Task 2");
CompletableFuture<String> f3 = CompletableFuture.supplyAsync(() -> "Task 3");

CompletableFuture<Void> allFutures = CompletableFuture.allOf(f1, f2, f3);

allFutures.thenRun(() -> {
try {
System.out.println(f1.get());
System.out.println(f2.get());
System.out.println(f3.get());
} catch (Exception e) {
e.printStackTrace();
}
});

anyOf() - First to Complete

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(2000); } catch (InterruptedException e) {}
return "Slow task";
});

CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(1000); } catch (InterruptedException e) {}
return "Fast task";
});

CompletableFuture<Object> firstCompleted = CompletableFuture.anyOf(future1, future2);
firstCompleted.thenAccept(System.out::println); // Fast task

Practical Examples

Parallel Processing

public CompletableFuture<String> fetchUserData(int userId) {
return CompletableFuture.supplyAsync(() -> {
// Simulate HTTP call
try { Thread.sleep(1000); } catch (InterruptedException e) {}
return "User data for ID: " + userId;
});
}

public CompletableFuture<String> fetchUserPosts(int userId) {
return CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(800); } catch (InterruptedException e) {}
return "Posts for user: " + userId;
});
}

// Parallel fetch
CompletableFuture<String> userData = fetchUserData(123);
CompletableFuture<String> userPosts = fetchUserPosts(123);

CompletableFuture<String> combined = userData.thenCombine(userPosts,
(data, posts) -> data + "\n" + posts);

Best Practices

  1. Non-blocking operations istifadə edin:

    // Blocking (pis)
    String result = future.get();

    // Non-blocking (yaxşı)
    future.thenAccept(System.out::println);
  2. Error handling unutmayın:

    future
    .thenApply(this::process)
    .exceptionally(ex -> "Default value");
  3. Custom executor istifadə edin:

    ExecutorService executor = Executors.newFixedThreadPool(4);
    CompletableFuture.supplyAsync(task, executor);
  4. join() vs get():

    // join() - unchecked exception
    String result = future.join();

    // get() - checked exception (try-catch lazım)
    String result = future.get();
  5. Timeout with Default:

    CompletableFuture<String> future = CompletableFuture
    .supplyAsync(() -> slowOperation())
    .completeOnTimeout("Default", 2, TimeUnit.SECONDS);

Common Patterns

Sequential vs Parallel

// Sequential (3 saniyə)
String result1 = task1(); // 1 saniyə
String result2 = task2(); // 1 saniyə
String result3 = task3(); // 1 saniyə

// Parallel (1 saniyə)
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> task1());
CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> task2());
CompletableFuture<String> f3 = CompletableFuture.supplyAsync(() -> task3());

CompletableFuture.allOf(f1, f2, f3).join();

Pipeline Processing

CompletableFuture<String> pipeline = CompletableFuture
.supplyAsync(() -> "input")
.thenApply(this::step1)
.thenApply(this::step2)
.thenApply(this::step3)
.exceptionally(ex -> "Error: " + ex.getMessage());

Conditional Processing

CompletableFuture<String> result = CompletableFuture
.supplyAsync(() -> getData())
.thenCompose(data -> {
if (data.isEmpty()) {
return CompletableFuture.completedFuture("No data");
}
return CompletableFuture.supplyAsync(() -> processData(data));
});

Üstünlükləri

  • Non-blocking - Thread-ləri blok etmir
  • Composable - Əməliyyatları birləşdirmək asan
  • Error handling - Exception-ları idarə etmək asan
  • Performance - Parallel işləmə imkanı
  • Readable - Kod oxunaqlıdır