CodeGym /Kurslar /JAVA 25 SELF /Future, CompletionHandler: əməliyyatların tamamlanmasının...

Future, CompletionHandler: əməliyyatların tamamlanmasının emalı

JAVA 25 SELF
Səviyyə , Dərs
Mövcuddur

1. Future: “Hazır olanda gözləyərəm”

Java-da asinxron əməliyyatlar — bu, məktubu poçtla göndərmək kimidir: siz poçt qutusunun yanında dayanıb cavabı gözləmir, öz işlərinizlə məşğul olursunuz. Məktub gələndə — sadəcə bildiriş alırsınız. Koddasa başa düşmək lazımdır ki, əməliyyat tamamlanıb və nəticəni və ya xətanı öyrənmək gərəkdir. Java-da bunun iki yanaşması var: FutureCompletionHandler.

Future ilə siz “qəbz” alırsınız — gələcəkdə nəticəyə “vəd”. Vaxtaşırı onun hazır olub-olmadığını yoxlaya və ya get() çağıraraq nəticəni gözləyə bilərsiniz.

CompletionHandler ilə isə ümumiyyətlə gözləmək lazım deyil: əvvəlcədən emalçı göstərirsiniz və o, uğur və ya xəta zamanı özü çağırılır.

Bu necə işləyir?

AsynchronousFileChannel üçün asinxron read() və ya write() metodunu çağırdığınızda, Future<Integer> tipli obyekt ala bilərsiniz. Bu, elektron növbədə talon kimidir: əməliyyat başladı və siz istənilən an soruşa bilərsiniz: “Bəs, hazırdır?”.

Metodun imzası

Future<Integer> read(ByteBuffer dst, long position)

və ya

Future<Integer> write(ByteBuffer src, long position)
  • dst — məlumatların oxunacağı bufer.
  • src — məlumatların yazılacağı bufer.
  • position — faylda oxuma/yazma mövqeyi.

Nümunə: Future ilə asinxron oxuma

Fayldan ilk 1024 baytı asinxron oxuyan və nəticəni açıq şəkildə gözləyən kod:

import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.Future;
import java.io.IOException;

public class FutureReadExample {
    public static void main(String[] args) {
        Path path = Path.of("example.txt");
        try (AsynchronousFileChannel channel = AsynchronousFileChannel.open(path, StandardOpenOption.READ)) {
            ByteBuffer buffer = ByteBuffer.allocate(1024);

            // Asinxron oxunuşu işə salırıq
            Future<Integer> future = channel.read(buffer, 0);

            // ... fayl oxunarkən burada başqa işlər görə bilərsiniz

            // Əməliyyatın başa çatmasını gözləyirik (bloklayan çağırış!)
            int bytesRead = future.get(); // InterruptedException, ExecutionException ata bilər

            System.out.println("Oxunan bayt sayı: " + bytesRead);

            // Buferdən oxumağa keçirik
            buffer.flip();
            while (buffer.hasRemaining()) {
                System.out.print((char) buffer.get());
            }
        } catch (Exception e) {
            System.err.println("Faylın oxunması xətası: " + e);
        }
    }
}
  • future.get() metodu əməliyyat tamamlanana qədər axını bloklayır — bu, “telefon yanında gözləmək” kimidir.
  • future.isDone() çağıraraq əməliyyatın başa çatıb-çatmamasını öyrənə və yalnız sonra get() çağırmaq olar.
  • Əgər əməliyyat xəta ilə bitərsə, get() ExecutionException atacaq (səbəb içindədir).

Future nə vaxt əlverişlidir?

Future o zaman uyğundur ki, siz tapşırığı işə salıb sonra, rahat vaxtda, nəticəni gözləmək istəyirsiniz: faylı oxumaq, sorğu etmək, fonda nəsə hesablamaq. Bu yanaşma bir neçə əməliyyatı paralel işə salıb nəticələri sonradan toplamaq üçün də münasibdir.

Amma əsl bloklamasız asinxronluq və tamamlanma barədə avtomatik bildiriş lazımdırsa, CompletionHandler daha uyğun ola bilər.

2. CompletionHandler: “Bitirəndə məni çağır”

CompletionHandler — sizin reallaşdırıb read() və ya write() metoduna ötürdüyünüz interfeysdir. Əməliyyat tamamlananda (və ya xəta baş verəndə) Java emalçınızı özü çağırır. Bu, nömrənizi qoymağa bənzəyir: “Hazır olanda zəng edin”.

Metodun imzası

void read(ByteBuffer dst,
          long position,
          A attachment,
          CompletionHandler<Integer, ? super A> handler)
  • dst — oxuma üçün bufer.
  • position — fayldakı mövqe.
  • attachment — emalçıya ötürüləcək ixtiyari obyekt (məsələn, null və ya fayl adı).
  • handler — sizin emalçınız.

CompletionHandler interfeysi

public interface CompletionHandler<V, A> {
    void completed(V result, A attachment);
    void failed(Throwable exc, A attachment);
}
  • completed uğur olduqda çağırılır; result — baytların sayı, attachment — sizin obyektiniz.
  • failed xəta zamanı çağırılır; exc — istisna.

Nümunə: CompletionHandler ilə asinxron oxuma

import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.io.IOException;

public class CompletionHandlerReadExample {
    public static void main(String[] args) throws IOException {
        Path path = Path.of("example.txt");
        AsynchronousFileChannel channel = AsynchronousFileChannel.open(path, StandardOpenOption.READ);

        ByteBuffer buffer = ByteBuffer.allocate(1024);

        channel.read(buffer, 0, "example.txt", new CompletionHandler<Integer, String>() {
            @Override
            public void completed(Integer result, String attachment) {
                System.out.println("Fayl " + attachment + " oxundu, bayt: " + result);
                buffer.flip();
                while (buffer.hasRemaining()) {
                    System.out.print((char) buffer.get());
                }
                closeChannel();
            }

            @Override
            public void failed(Throwable exc, String attachment) {
                System.err.println("Faylın oxunmasında xəta " + attachment + ": " + exc);
                closeChannel();
            }

            private void closeChannel() {
                try {
                    channel.close();
                } catch (IOException e) {
                    System.err.println("Kanalın bağlanması xətası: " + e);
                }
            }
        });

        // Vacibdir: main metodu oxu bitmədən başa çata bilər, əgər axını “saxlamasanız”!
        // Real tətbiqlərdə axın adətən dərhal bitmir (məsələn, server, UI).
        try {
            Thread.sleep(500); // Asinxron əməliyyata vaxt verək (yalnız nümunə üçün!)
        } catch (InterruptedException ignored) {}
    }
}

read() metoduna tamamlandıqda nə ediləcəyini bilən anonim emalçı ötürürük. completed içində nəticəni alıb emal edirik, failed isə xəta zamanı reaksiya verir. Açıq resurs qoymamaq üçün kanalı emalçının içində bağlamağı unutmayın.

Bir incə nüans var: main metodu asinxron əməliyyat bitməmiş başa çata bilər, buna görə nümunədə nəticəni görmək üçün Thread.sleep(500) əlavə edilib — yalnız nümayiş məqsədi ilə. Real tətbiqlərdə bu fənd adətən lazım olmur.

CompletionHandler nə vaxt daha münasibdir?

CompletionHandler gözləməsiz və bloklamasız əsl asinxronluq lazım olanda uyğundur: əməliyyatı işə salırsınız və tamamlandıqda nə ediləcəyini təsvir edirsiniz. Bu, UI (JavaFX, Swing) üçün vacibdir ki, interfeys “donmasın”, həmçinin serverlərdə faydalıdır — axınlar gözləmə vəziyyətində qalmır, yalnız iş olanda istifadə olunur.

4. Future və CompletionHandler müqayisəsi

Yanaşma Axını bloklayır? Nə vaxt istifadə etməli? Nümunə ssenari
Future Bəli ( get() zamanı) Sadə ardıcıl işləmə Faylın kopyalanması, hesabatlar
CompletionHandler Xeyr Truly async, UI, server, paralel əməliyyatlar Server, GUI, kütləvi IO
  • Future — anlamaq daha asandır, lakin nəticəni almaq üçün bloklama tələb edir.
  • CompletionHandler — bir az mürəkkəbdir, lakin əsl asinxronluq verir və axınları bloklamır.

5. Təcrübə: CompletionHandler ilə fayla asinxron yazma

Nəticəni bildirərək sətiri fayla asinxron yazan nümunə:

import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

public class CompletionHandlerWriteExample {
    public static void main(String[] args) throws IOException {
        String text = "Salam, asinxron dünya!";
        ByteBuffer buffer = ByteBuffer.wrap(text.getBytes(StandardCharsets.UTF_8));
        Path path = Path.of("async_output.txt");

        AsynchronousFileChannel channel = AsynchronousFileChannel.open(
                path, StandardOpenOption.WRITE, StandardOpenOption.CREATE);

        channel.write(buffer, 0, path, new CompletionHandler<Integer, Path>() {
            @Override
            public void completed(Integer result, Path attachment) {
                System.out.println("Uğurla " + result + " bayt yazıldı: " + attachment);
                try {
                    channel.close();
                } catch (IOException e) {
                    System.err.println("Kanalın bağlanması xətası: " + e);
                }
            }

            @Override
            public void failed(Throwable exc, Path attachment) {
                System.err.println("Fayla yazmada xəta " + attachment + ": " + exc);
                try {
                    channel.close();
                } catch (IOException e) {
                    System.err.println("Kanalın bağlanması xətası: " + e);
                }
            }
        });

        // Nəticəni görmək üçün main-i bir az saxlayacağıq (yalnız nümunə üçün)
        try {
            Thread.sleep(500);
        } catch (InterruptedException ignored) {}
    }
}
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION