Sobes.tech
Middle

Πώς μπορεί να οργανωθεί η ανταλλαγή δεδομένων μεταξύ δύο νημάτων σε Java;

sobes.tech AI

Απάντηση από AI

Οι τρόποι ανταλλαγής δεδομένων μεταξύ νημάτων στη Java μπορούν να οργανωθούν με τους ακόλουθους τρόπους:

  • Κοινές μεταβλητές με συγχρονισμό: Τα νήματα χρησιμοποιούν κοινές μεταβλητές, στις οποίες η πρόσβαση συγχρονίζεται με τη λέξη-κλειδί synchronized, μπλοκ (Lock από java.util.concurrent.locks) ή ατομικές μεταβλητές (Atomic από java.util.concurrent.atomic).
  • Διαδρομές δεδομένων (Piped Streams): Συνδεδεμένες ροές εισόδου/εξόδου (PipedInputStream και PipedOutputStream) επιτρέπουν σε μια ροή να γράφει δεδομένα, ενώ μια άλλη να τα διαβάζει.
    // Χρήση Piped Streams
    try {
        PipedOutputStream pout = new PipedOutputStream();
        PipedInputStream pin = new PipedInputStream(pout);
    
        // Ροή 1: εγγραφή
        new Thread(() -> {
            try {
                pout.write("Γειά σου από το νήμα 1".getBytes());
                pout.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
    
        // Ροή 2: ανάγνωση
        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();
    }
    
  • Ουρές (Queues): Η χρήση ασφαλών για νήματα συλλογών, όπως το BlockingQueue (π.χ., ArrayBlockingQueue, LinkedBlockingQueue), επιτρέπει σε ένα νήμα να τοποθετεί στοιχεία στην ουρά, και σε ένα άλλο να τα εξάγει.
    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.LinkedBlockingQueue;
    
    // Χρήση BlockingQueue
    BlockingQueue<String> queue = new LinkedBlockingQueue<>();
    
    // Παραγωγός
    new Thread(() -> {
        try {
            queue.put("Μήνυμα 1");
            queue.put("Μήνυμα 2");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }).start();
    
    // Καταναλωτής
    new Thread(() -> {
        try {
            System.out.println(queue.take());
            System.out.println(queue.take());
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }).start();
    
  • Μηχανισμοί wait(), notify(), notifyAll(): Χρησιμοποιούνται για συγχρονισμό νημάτων με βάση τους παρατηρητές αντικειμένων. Ένα νήμα καλεί wait() για να περιμένει μια συνθήκη, ενώ ένα άλλο καλεί notify() ή notifyAll() για να ειδοποιήσει τα αναμένοντα νήματα για την αλλαγή της συνθήκης.
    // Χρήση wait/notify
    class Data {
        private String message;
        private boolean empty = true;
    
        public synchronized String read() {
            while (empty) {
                try {
                    wait(); // Περιμένουμε δεδομένα
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
            empty = true;
            notifyAll(); // Ειδοποιούμε για δυνατότητα εγγραφής
            return message;
        }
    
        public synchronized void write(String message) {
            while (!empty) {
                try {
                    wait(); // Περιμένουμε μέχρι να διαβαστούν τα προηγούμενα δεδομένα
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
            empty = false;
            this.message = message;
            notifyAll(); // Ειδοποιούμε για νέα δεδομένα
        }
    }
    
  • CompletableFuture: Επιτρέπει τη δημιουργία ασύγχρονων υπολογισμών και τον συνδυασμό των αποτελεσμάτων τους, διασφαλίζοντας ανταλλαγή δεδομένων μέσω των επιστρεφόμενων τιμών και των χειριστών ολοκλήρωσης.
  • Exchanger: Επιτρέπει σε δύο νήματα να ανταλλάσσουν αντικείμενα σε σημείο συγχρονισμού.

Η επιλογή συγκεκριμένης μεθόδου εξαρτάται από τη φύση της εργασίας, το απαιτούμενο επίπεδο συγχρονισμού, την απόδοση και την πολυπλοκότητα υλοποίησης.