Problem bei kleiner Thread-Übung

vandread

Aktives Mitglied
Hallo Leute,

ich stehe vor einem kleinen Problem bei einer kleinen Übungsaufgabe bei der es um Threads geht.
Die Aufgabe sieht ganz grob etwa so aus:
Es soll eine Klasse geben die einen Stack darstellen soll in der int Werte gespeichert werden können, natürlich hat diese die klassischen Methoden pop() und push(int val) also ein LIFO.
Dann soll es zwei klassen geben die den Stack lesen und füllen, diese beiden Klassen sollen Threads sein...
Das Programm soll so ablaufen, dass man einen Stack, Reader und Writer erstellt, dann startet man den Reader und den Writer.
Der Reader soll ununterbrochen laufen!
Der Writer soll 10 Werte schreiben und anschließend ist er fertig und dann soll auch das Programm zu ende gehen!

(Ich habe jetzt die Aufgabe sehr grob beschrieben)

So sehen meine Klassen aus:
(Das grobe Gerüst der Klassen war (leider) gegeben)

Java:
public class Main {

    public static void main(String[] args) {
        Stack s = new Stack(5);

        Thread w = new Writer(s);
        Thread r = new Reader(s);

        w.start();
        r.start();

        while (w.isAlive()) {
        }

        System.exit(0);
    }
}

Java:
public class Stack {

    private int[] refIntarr;
    private int top = 0;

    public Stack(int size) {
        refIntarr = new int[size];
    }

    public void print() {
        for (int i = 0; i < top; i++) {
            System.out.print("[" + refIntarr[i] + "] ");
        }

        System.out.println();
    }

    public void push(int val) throws OverflowException {
        if (top >= refIntarr.length) {
            throw new OverflowException();
        } else {
            refIntarr[top] = val;
            top++;
        }
    }

    public int pop() throws UnderflowException {
        if (top <= 0) {
            throw new UnderflowException();
        } else {  
            int temp = refIntarr[top - 1];
            top--;
            
            return temp;
        }
    }
}

Java:
import java.util.logging.Level;
import java.util.logging.Logger;


public class Writer extends Thread {

    private Stack stackref = null;

    public Writer(Stack stackref) {
        this.stackref = stackref;
    }
        
    @Override
    public void run() {
        int zahl;
        
        for (int i = 0; i < 10; i++) {
            zahl = (int)(Math.random()*10);
            
            try {
                stackref.push(zahl);
            } catch (OverflowException ex) {
                System.out.println("Writer: " + ex.getMessage());
            }
            
            System.out.print("Writer ~ print(): ");
            stackref.print();
            
            try {
                sleep(3);
            } catch (InterruptedException ex) {
                Logger.getLogger(Writer.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
    
}

Java:
import java.util.logging.Level;
import java.util.logging.Logger;

public class Reader extends Thread {

    private Stack stackref = null;

    public Reader(Stack stackref) {
        this.stackref = stackref;
    }

    @Override
    public void run() {
        while (true) {
            System.out.print("Reader ~ print(): ");
            stackref.print();

            try {
                System.out.println("pop(): " + stackref.pop());
            } catch (UnderflowException ex) {
                System.out.println("Reader: " + ex.getMessage());
            }
            
            try {
                sleep(3);
            } catch (InterruptedException ex) {
                Logger.getLogger(Reader.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}

So nun zu meinem kleinen Problem...
Im prinzip funktioniert das Programm und tut auch was es soll, doch das Problem ist folgendes:
Die Aufgabe verlangt das wenn eine Zahl in den Stack gelegt wird, der Stack ausgegeben werden soll,
das gleiche soll passieren wenn eine Zahl aus dem Stack genommen wird...
Hier wird nun Mischmasch...
Die Ausgabe kommt mit den Threads nicht hinterher, also zB Writer schreibt eine Zahl rein will dann den Stack ausgeben doch Reader hat die Zahl schon gelesen dh der Stack ist leer usw...

So also kurz gesagt mein Stack ist nicht Thread-Safe...
Doch ich weiß nicht genau wie ich das nun machen soll "synchronized" hilft mir ja auch nicht weiter da ja beide Threads auf unterschiedliche Methoden zugreifen...

Ich hab mir überlegt eine Variable im Stack zu definieren die darstellt ob gerade ein Zugriff auf den Stack erfolgt...

Aber das ist glaube ich nicht der richte Ansatz...
Wie würdet ihr das Problem lösen, natürlich auf guter Java Art 🙂
 
Vielen Dank! 🙂

Ich habe total vergessen das es die Möglichkeit gibt einen synchronized-Block zu erstellen und ein bestimmtes Objekt zu blocken...

Hier die Änderungen an den entsprechenden Klassen:

Java:
import java.util.logging.Level;
import java.util.logging.Logger;

public class Writer extends Thread {

    private Stack stackref = null;

    public Writer(Stack stackref) {
        this.stackref = stackref;
    }

    @Override
    public void run() {
        int zahl;

        for (int i = 0; i < 10; i++) {
            zahl = (int) (Math.random() * 10);

            synchronized (stackref) {
                try {
                    stackref.push(zahl);
                } catch (OverflowException ex) {
                    System.out.println("Writer: " + ex.getMessage());
                }

                System.out.print("Writer ~ print(): ");
                stackref.print();
            }

            try {
                sleep(3);
            } catch (InterruptedException ex) {
                Logger.getLogger(Writer.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}

Java:
import java.util.logging.Level;
import java.util.logging.Logger;

public class Reader extends Thread {

    private Stack stackref = null;

    public Reader(Stack stackref) {
        this.stackref = stackref;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (stackref) {
                System.out.print("Reader ~ print(): ");
                stackref.print();

                try {
                    System.out.println("pop(): " + stackref.pop());
                } catch (UnderflowException ex) {
                    System.out.println("Reader: " + ex.getMessage());
                }
            }

            try {
                sleep(3);
            } catch (InterruptedException ex) {
                Logger.getLogger(Reader.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}

Kleine Frage aber noch...
NetBeans gibt mir eine Warnung aus, weil das Objekt "stackref" nicht final ist...
Ich kann mir schon denken das er mich Warnt weil es sein könnte dass zB bei einem lock dieses Objekt von wo anders geändert wird und stackref auf was anderes verweist...

Aber ist es nun "unschön" ein nicht final Objekt bei synchronized zu nutzen oder ist das einfach nur "aber bitte vergiss nicht das ..." ?

Danke!
 

Zurück
Oben