Ich habe versucht, einen Reader-Writer mit notify und wait zu implementieren. Aber ich glaube ich stecke fest. Meine Sequenz geht so. RRRRRRRRRRWWWWWWWWW Dies geschieht, wenn der Hauptstart mit dem zuerst aufgerufenen Reader beginnt. Oder WWWWWWWRRRRRRRRRRR. Dies geschieht, wenn der Hauptstart mit dem zuerst aufgerufenen Writer beginnt. Es sieht so aus, als ob Lesebenachrichtigungen überhaupt nicht funktionieren. Writer-Thread wird nie ausgeführt.

Wenn ich while-Schleife in der Run-Methode mache, um unendlich zu laufen, dann ist es einfach RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR......... Keine Chance für den Schriftsteller zu schreiben. Können Sie sich das ansehen?

DATENKLASSE

public class Data {
    private int q ;
    private boolean isAnyOneReading;

    public Data() {
    }

    public  void readQ() {
        synchronized (this){
            isAnyOneReading = true;
            System.out.println("Read start "+q);
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        synchronized (this){
            isAnyOneReading = false;
            System.out.println("Read end "+q);
            notifyAll();
        }
    }

    public synchronized void writeQ(int q) {
        System.out.println(isAnyOneReading);
        while (isAnyOneReading){
            try{
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("Done");
                Thread.currentThread().interrupt();
            }
        }
        System.out.println("Write start "+q);
        this.q = q;
        try{
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
            Thread.currentThread().interrupt();
        }
        System.out.println("Write end "+q);
        notifyAll();
    }
}

LESERKLASSE

public class Reader implements  Runnable {
    private Data data;
    private Thread readerThread;


    public Reader(Data data) {
        this.data = data;
        readerThread = new Thread(this, "ReaderThread");
    }


    void startThread(){
        readerThread.start();
    }

    @Override
    public void run() {
        int i = 0 ;
        while (i != 5){
            data.readQ();
            i++;
        }
    }
}

SCHREIBERKLASSE

public class Writer  implements  Runnable{
    private Data data;
    private Thread writerThread;

    public Writer(Data data) {
        this.data = data;
        writerThread = new Thread(this,"WriterThread," );
    }

    void startThread(){
        writerThread.start();
    }

    @Override
    public void run() {
        int i = 0 ;
        int j = 0 ;
        while (j != 5){
            data.writeQ(i++);
           // i++;
           j++;
        }
    }
}

HAUPTKLASSE

public class ReaderWriterDemo {
    public static void main(String[] args) {
        Data data = new Data();
        Reader reader = new Reader(data);
        Writer writer = new Writer(data);

        reader.startThread();
        writer.startThread();



    }
}
1
Shreya 23 Feb. 2020 im 09:23

3 Antworten

Sieht aus wie ein einfacher Fall von Hunger. Betrachten Sie die Hauptschleife Ihres Autors:

    while (j != 5){
        data.writeQ(i++);
       // i++;
       j++;
    }

data.writeQ() ist eine synchronized Methode: Das allerletzte, was es tut, bevor es zurückkehrt, ist das Entsperren der Sperre. Das allererste, was es beim nächsten Anruf tut, ist, die Sperre erneut zu sperren. Dazwischen passiert nicht viel - das Inkrementieren und Testen einer lokalen Variablen ist alles.

Java synchronized -Sperren sind nicht fair . (Wenn also eine Sperre verfügbar wird, garantiert das System nicht , dass der Gewinner der Thread ist, der am längsten gewartet hat.) Tatsächlich kann es sich um das Gegenteil of fair: Das Betriebssystem versucht möglicherweise, die effiziente Nutzung der CPU (s) zu maximieren, indem es immer den Thread auswählt, der am einfachsten zu aktivieren ist.

Wenn der Schreiber bei jeder nachfolgenden Iteration zurückkommt, um data.writeQ() aufzurufen, hat das Betriebssystem möglicherweise noch nicht einmal gestartet , um den Leser zu wecken, und lässt den Schreiber einfach die { {X1}} erneut blockieren.


Das gleiche passiert mit Ihrem Leser. Der Code ist etwas komplizierter, aber genau wie beim Writer ist das allerletzte, was data.readQ() vor der Rückkehr tut, das Entsperren der Sperre und das allererste, was es beim nächsten Aufruf tut, das Sperren nochmal.


Brute-Force-Lösung: Ersetzen Sie die synchronized -Blöcke durch ein faires ReentrantLock Objekt.

Alternative Lösung, die typischer für die tatsächliche Funktionsweise vieler Programme ist: Lassen Sie die Threads zwischen den Aufrufen der gesperrten Funktion etwas anderes ausführen (z. B. einige E / A-Vorgänge ausführen), und geben Sie dabei die andere Threads eine Chance, in die gesperrte Ressource einzusteigen und sie zu nutzen.

0
Solomon Slow 24 Feb. 2020 im 00:18

Reads notifyAll () funktioniert, aber es scheint, dass read () erneut aufgerufen wird und den Wert von AnyOneReading vor jeder anderen Aktion in write () ändert. Aus diesem Grund schlägt die Prüfung fehl und write () wartet erneut. Wie Danny Fried vorgeschlagen hat, hilft es, Thread.sleep () auf die Ausführungsmethoden zu verschieben.

0
busterbrown 23 Feb. 2020 im 08:58

Versuchen Sie, Thread.sleep aus der Datenklasse zu entfernen. Und fügen Sie Thread.sleep in solchen Ausführungsmethoden hinzu. (Ein Beispiel einfügen):

 @Override
public void run() {
    int i = 0;
    while (i != 5) {
        data.readQ();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            i++;
        }
    }
}
0
Danny Fried 23 Feb. 2020 im 08:36