Sobes.tech
Middle

Hoe organiseer je gegevensuitwisseling tussen twee threads in Java?

sobes.tech AI

Antwoord van AI

De gegevensuitwisseling tussen threads in Java kan op de volgende manieren worden georganiseerd:

  • Gedeelde variabelen met synchronisatie: Threads gebruiken gedeelde variabelen, waarvan de toegang wordt gesynchroniseerd met behulp van het sleutelwoord synchronized, vergrendelingen (Lock uit java.util.concurrent.locks) of atomische variabelen (Atomic uit java.util.concurrent.atomic).
  • Datastreams (Aangesloten streams): Verbonden invoer-/uitvoerstreams (PipedInputStream en PipedOutputStream) stellen één thread in staat gegevens te schrijven en een andere te lezen.
    // Gebruik van verbonden streams
    try {
        PipedOutputStream pout = new PipedOutputStream();
        PipedInputStream pin = new PipedInputStream(pout);
    
        // Thread 1: schrijven
        new Thread(() -> {
            try {
                pout.write("Hallo van thread 1".getBytes());
                pout.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
    
        // Thread 2: lezen
        new Thread(() -> {
            try {
                int data;
                while ((data = pin.read()) != -1) {
                    System.out.print((char) data);
                }
                pin.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
    } catch (Exception e) {
        e.printStackTrace();
    }
    
  • Wachtrijen (Queues): Het gebruik van thread-veilige collecties zoals BlockingQueue (bijvoorbeeld ArrayBlockingQueue, LinkedBlockingQueue) stelt één thread in staat elementen in de wachtrij te plaatsen en een andere ze te verwijderen.
    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.LinkedBlockingQueue;
    
    // Gebruik van BlockingQueue
    BlockingQueue<String> queue = new LinkedBlockingQueue<>();
    
    // Producent
    new Thread(() -> {
        try {
            queue.put("Bericht 1");
            queue.put("Bericht 2");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }).start();
    
    // Consument
    new Thread(() -> {
        try {
            System.out.println(queue.take());
            System.out.println(queue.take());
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }).start();
    
  • Mechanismen wait(), notify(), notifyAll(): Worden gebruikt om threads te synchroniseren op basis van objectmonitors. Een thread roept wait() aan om te wachten op een voorwaarde, een andere roept notify() of notifyAll() aan om de wachtende threads te informeren over een verandering in de voorwaarde.
    // Gebruik van wait/notify
    class Data {
        private String message;
        private boolean empty = true;
    
        public synchronized String read() {
            while (empty) {
                try {
                    wait(); // Wachten op gegevens
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
            empty = true;
            notifyAll(); // Informeren dat schrijven mogelijk is
            return message;
        }
    
        public synchronized void write(String message) {
            while (!empty) {
                try {
                    wait(); // Wachten tot de vorige gegevens zijn gelezen
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
            empty = false;
            this.message = message;
            notifyAll(); // Informeren over nieuwe gegevens
        }
    }
    
  • CompletableFuture: Maakt het mogelijk om asynchrone berekeningen te maken en deze te combineren, waarbij gegevens worden uitgewisseld via teruggekeerde waarden en voltooiingshandlers.
  • Exchanger: Stelt twee threads in staat om objecten uit te wisselen op een synchronisatiepunt.

De keuze van een specifieke methode hangt af van de aard van de taak, het vereiste synchronisatieniveau, de prestaties en de complexiteit van de implementatie.