Vorschläge für Umstellung auf Thread-Nutzung erwünscht

daWonderer

Aktives Mitglied
Hallo, nach über 15 Jahren Abstinenz und als Bloody-Beginner hab ich mich heut mal rangesetzt und ein kleines Projekt geschrieben.

Ich möchte einen Kaffee-Simulator programmieren.

das Folgende hab ich jetzt:

Java:
public class Liquid {
    public String liquidType;

    public Liquid( String name )
    {
        liquidType = name;
    }
}

Java:
import java.util.ArrayList;

public class LiquidContainer {
    public String containerName;
    public int capacity;
    public int fillLevel;
    public ArrayList filledWith = new ArrayList<>();

    public LiquidContainer( String containerName, int c )
    {
        this.containerName = new String(containerName);
        capacity = c;      
    }
    public int fillWith( Liquid l, int mililiter )
    {
        int fillAmount = capacity-fillLevel;
        if( mililiter < fillAmount )
            fillAmount = mililiter;

        int i;
        boolean same = false;
        for( i=0;i<filledWith.size();i+=2)
        {
            Liquid li = (Liquid)filledWith.get(i);
            int mili = ((int)filledWith.get(i+1));
            if( li.liquidType == l.liquidType )
            {
                mili += fillAmount;
                filledWith.remove(i+1);
                filledWith.add(i+1,mili);
                same = true;
                break;
            }
        }
        if( !same )
        {
            filledWith.add(l);
            filledWith.add(fillAmount);
        }
        System.out.println(containerName+" filled "+String.valueOf(fillAmount));
        fillLevel+=fillAmount;

        return fillAmount;
    }
    public void consume()
    {
        filledWith = new ArrayList<>();
        fillLevel = 0;
        System.out.println(containerName+" consumed..");
    }
    public void fillInto( LiquidContainer lc, int amount)
    {
        if( amount > lc.capacity )
            amount = lc.capacity;

        for( int i=0;i<filledWith.size();i+=2)
        {
            Liquid l = (Liquid)filledWith.get(i);
            int mili = (int)filledWith.get(i+1);

            float perc = (float)mili/fillLevel;
            int fill = (int)((float)amount*perc);
            lc.fillWith(l, fill);
           
            mili -= fill;
            filledWith.remove(i+1);
            filledWith.add(i+1,mili);          
        }
        fillLevel -= amount;
    }
    public String getFillingStr()
    {
        String s = new String();
        if( filledWith.size()>2)
            s = s.concat("mixed with:\n");
        for( int i=0;i<filledWith.size();i+=2)
        {
            Liquid l = (Liquid)filledWith.get(i);
            int ml = (int)filledWith.get(i+1);

            s = s.concat(l.liquidType+" "+String.valueOf(ml)+"ml");
            s = s.concat(new String("\n"));
        }
        if( s.equals("") )
        {
            s = s.concat("empty");
        }
        return s;
    }
}

und das Hauptprogramm:

Java:
import java.time.Instant;

public class App {
    public static void main(String[] args) throws Exception {
        Liquid coffee = new Liquid("Coffee");

        LiquidContainer coffeePot = new LiquidContainer("Pot",500);
        LiquidContainer cup = new LiquidContainer("Cup",50);

        coffeePot.fillWith(coffee,250);
        System.out.println("Pot:"+coffeePot.getFillingStr());

        coffeePot.fillInto(cup, 100);

        //System.out.println("Filling:");
        System.out.println("Cup:"+cup.getFillingStr());

        long nowCompare = Instant.now().getEpochSecond();

        do
        {
            long now = Instant.now().getEpochSecond();

            if( now - nowCompare > 10 )
            {
                cup.consume();
                coffeePot.fillInto(cup,50);

                System.out.println("Pot contains: "+coffeePot.getFillingStr());
                nowCompare = now;
            }
        }while(coffeePot.fillLevel>0);
        System.out.println("Coffeepot is empty!");

        cup.consume();

        /*
        Liquid milk = new Liquid("Milk");
       
        cup.fillWith(coffee, 25);
        cup.fillWith(milk, 25);
        System.out.println("Cup1 contains:");
        System.out.println(cup.getFillingStr());

        LiquidContainer cup2 = new LiquidContainer("Cup2", 50);
        cup.fillInto(cup2, 20);
        System.out.println("Cup1 contains:");
        System.out.println(cup.getFillingStr());
        System.out.println("Cup2 contains:");
        System.out.println(cup2.getFillingStr());
        */
    }
}

Gerade ebend hab ich mir auf Oracle das Tutorial für Threads durchgelesen. Ich bin mir aber jetzt unsicher wie ich das Hauptprogramm jetzt umstelle.
Am liebsten wäre es mir wenn das Kaffeekochen eine Weile dauert und das Konsumieren des Tasseninhaltes auch eine Weile.
Was habt ihr für Vorschläge dazu?
auch wenn ihr allgemein was anmerken wollt zu meiner Grundidee und den Klassen nehme ich das gern entgegen!

Grüße aus Sachsen..
 
Mal was grundsätzliches zu Multithreading: Das ist nur was für Aufgaben, die sich parallelisieren lassen. Ein paar Beispiele:
-Du hast eine (sehr lange) Liste mit Objekten, die du irgendwie verarbeiten willst, wobei die Verarbeitung eines Objekts unabhängig von den anderen Objekten ist. Dann kannst du die Liste z.B. in zwei Teile zerlegen und die Verarbeitung in zwei Threads ausführen, wobei jeder Thread für jeweils eine Hälfte der Liste zuständig ist. Oder du viertels die Liste und hast vier Threads. Oder...
-Manche Rechenoperationen lassen sich gut zerlegen, z.B. Integrale. Es gibt z.B. eine Rechenvorschrift um Pi zu berechnen, wobei ein Integral über einen Viertelkreis in lauter schmale Streifen zerschnitten wird, wobei sich Pi umso genauer berechnen läßt je mehr Streifen man hat. Je mehr Streifen, desto mehr Threads kann man länger beschäftigen.
-Viele 3D- und Graphikberechnungen lassen sich auch gut parallelisieren. Graphikkarten sind im Prinzip nichts anderes als relativ langsame Prozessoren mit relativ wenigen Funktionen (soweit ich weiß meist keine Fließkommaberechnungen und ich weiß nichtmal ob überhaupt 64 Bit unterstützt werden, wenn ja dann war das jedenfalls sehr lange noch nicht so obwohl CPUs das schon jahrelang vorher konnten), dafür sind aber mehrere hundert bis mehrere tausend solcher Rechenkerne auf einem Chip drauf.

Zu deinem Problem: Ich sehe nicht was man da parallelisieren könnte. Du hast immer eine Bedingung, die erst erfüllt sein muß bevor du weitermachen kannst. Und du hast Wartezeiten...das kann man so auch nicht parallelisieren. Ich würde mir an deiner Stelle ein neues Problem suchen und zur Übung lösen. Vielleicht ist da was dabei:
 
Zunächst solltest du genauer sein:
Java:
public class Liquid {
    public String liquidType;

    public Liquid( String name )
    {
        liquidType = name;
    }
}
public Liquid( String name ) - Was ist hier gemeint? name oder liquidType - das ergibt keinen Sinn. Name kann natürlich ein String sein, ein Typ wäre eher eine begrenzte Menge also: Enum oder Konstanten. Was fehlt ist auch eine Validierung, hier kann NULL übergeben werden. Der 'name' sollte hier auch unveränderlich sein.
Java:
/**
 *
 * @Description: ...
 */
public class Liquid {

    public final String name;

    public Liquid(final String name) {
        this.name = Objects.requireNonNull(name, "Liquid name must not be null");
    }

    public String getLiquidName() {
        return this.name;
    }

    @Override
    public String toString() {
        return String.format("Liquid{ \"name=\"%s\" }", this.name);
    }
}

Strings kopieren ist hier völlig unnötig:
Java:
...
this.containerName = new String(containerName);
...
System.out.println(containerName+" filled "+String.valueOf(fillAmount));
Strings sind unveränderlich! Die vermeintliche Änderung erzeugt also immer eine neue Instanz eines Strings, der ursprüngliche String bleibt unverändert.

Strings vergleicht man nicht mit ==:
Java:
if( li.liquidType == l.liquidType )
sondern mit String.equals(...) andernfalls kann das zu unerwarteten Ergebnissen führen. Ich bevorzuge:
Java:
if (Objects.equals(li.getLiquidName(), l.getLiquidName())) {

Da ich schreibfaul bin, habe ich das mal eine KI machen lassen:
Dein Code verwendet eine ArrayList, um Flüssigkeiten (Liquid) und deren Mengen zu speichern. Dabei wird die ArrayList als roher Typ verwendet, was zu den Warnungen führt.
1. Rohtyp ArrayList:
- Die ArrayList ist nicht typisiert, was zu Warnungen führt und die Typsicherheit beeinträchtigt.
- Es wird viel Casting benötigt, was fehleranfällig ist.

2. Unübersichtliche Datenstruktur:
- Die ArrayList speichert Flüssigkeiten und Mengen abwechselnd, was schwer zu verwalten ist.
- Eine bessere Lösung wäre die Verwendung einer Map<Liquid, Integer>.


### Verbesserte Version:
Hier ist eine überarbeitete Version der LiquidContainer-Klasse, die diese Probleme behebt:

Java:
public class LiquidContainer {

    public String containerName;

    public int capacity;

    public int fillLevel;

    public Map<Liquid, Integer> filledWith = new HashMap<>();


    public LiquidContainer(final String containerName, final int capacity) {

        this.containerName = containerName;

        this.capacity = capacity;

    }


    public int fillWith(final Liquid liquid, final int milliliter) {

        int fillAmount = this.capacity - this.fillLevel;

        if (milliliter < fillAmount) {

            fillAmount = milliliter;

        }


        this.fillLevel += fillAmount;


        // Flüssigkeit und Menge in die Map einfügen oder aktualisieren

        filledWith.merge(liquid, fillAmount, Integer::sum);


        System.out.println(this.containerName + " filled " + fillAmount + "ml of " + liquid.getLiquidName());

        return fillAmount;

    }


    public void consume() {

        filledWith.clear();

        fillLevel = 0;

        System.out.println(this.containerName + " consumed.");

    }


    public void fillInto(final LiquidContainer targetContainer, int amount) {

        if (amount > targetContainer.capacity - targetContainer.fillLevel) {

            amount = targetContainer.capacity - targetContainer.fillLevel;

        }


        for (Map.Entry<Liquid, Integer> entry : filledWith.entrySet()) {

            Liquid liquid = entry.getKey();

            int availableAmount = entry.getValue();


            float percentage = (float) availableAmount / this.fillLevel;

            int transferAmount = (int) (amount * percentage);


            targetContainer.fillWith(liquid, transferAmount);


            filledWith.put(liquid, availableAmount - transferAmount);

        }


        this.fillLevel -= amount;

    }


    public String getFillingStr() {

        StringBuilder sb = new StringBuilder();

        if (filledWith.isEmpty()) {

            sb.append("empty");

        } else {

            if (filledWith.size() > 1) {

                sb.append("mixed with:\n");

            }

            for (Map.Entry<Liquid, Integer> entry : filledWith.entrySet()) {

                sb.append(entry.getKey().getLiquidName())

                  .append(" ")

                  .append(entry.getValue())

                  .append("ml\n");

            }

        }

        return sb.toString();

    }

}


### Änderungen:
1. Verwendung von Map<Liquid, Integer>:
- Die Map speichert Flüssigkeiten (Liquid) als Schlüssel und deren Mengen (Integer) als Werte.
- Dadurch entfällt die Notwendigkeit, Flüssigkeiten und Mengen abwechselnd in einer ArrayList zu speichern.

2. Typisierung:
- Die Verwendung von Generics (Map<Liquid, Integer>) sorgt für Typsicherheit und vermeidet Casting.

3. getLiquidName() statt liquidType:
- Der Zugriff auf den Namen der Flüssigkeit erfolgt über die Methode getLiquidName() der Liquid-Klasse.

4. Effizientere Methoden:
- Die Methode fillWith verwendet merge, um Flüssigkeiten und Mengen in der Map zu aktualisieren.
- Die Methode fillInto berechnet den Anteil der Flüssigkeit basierend auf der Gesamtfüllmenge und überträgt diese in einen anderen Container.

Im Anschluss noch zu deiner Frage mit den Threads. Mit klassischen Threads würde ich etwa so vorgehen:
Java:
public class CoffeeMaker implements Runnable {

    private final Liquid liquid;
    private final int brewingTime; // in milliseconds

    public CoffeeMaker(final Liquid liquid, final int brewingTime) {
        this.liquid = liquid;
        this.brewingTime = brewingTime;
    }

    public void brewCoffee() {
        System.out.println("Starting to brew coffee with " + this.liquid.getLiquidName() + "...");
        final Thread thread = new Thread(this);
        thread.start();
    }

    @Override
    public void run() {
        try {
            Thread.sleep(this.brewingTime);
            System.out.println("Coffee is ready! Enjoy your " + this.liquid.getLiquidName() + " coffee.");
        } catch (final InterruptedException e) {
            System.err.println("Brewing process was interrupted.");
            Thread.currentThread().interrupt();
        }
    }

    public static void main(final String[] args) {
        final Liquid water = new Liquid("Water");
        final CoffeeMaker coffeeMaker = new CoffeeMaker(water, 5000); // 5 seconds brewing time
        coffeeMaker.brewCoffee();
    }
}


### Erklärung:
1. CoffeeMaker-Klasse:
- Die Klasse nimmt ein Liquid-Objekt und eine Kochzeit (brewingTime) in Millisekunden entgegen.
- Die Methode brewCoffee() startet einen neuen Thread, der den Kochvorgang simuliert.

2. run()-Methode:
- Simuliert die Dauer des Kochens mit Thread.sleep(brewingTime).
- Gibt eine Nachricht aus, wenn der Kaffee fertig ist.

3. main()-Methode:
- Erstellt ein Liquid-Objekt (z. B. Wasser) und startet den Kochvorgang.

### Beispielausgabe:
Code:
Starting to brew coffee with Water...
Coffee is ready! Enjoy your Water coffee.

Code:
Starting to brew coffee with Water...
Coffee is ready! Enjoy your Water coffee.

Allerdings wird das im Kontext eines Programms schnell unübersichtlich und schwer zu integriren. Daher würde ich die
ExecutorService-API verwenden. Vorteile sind weniger Code, mehr Übersicht, weniger Probleme mit der Synchronisation von Threads.
 
Zuletzt bearbeitet:
Mal was grundsätzliches zu Multithreading: Das ist nur was für Aufgaben, die sich parallelisieren lassen. Ein paar Beispiele:
-Du hast eine (sehr lange) Liste mit Objekten, die du irgendwie verarbeiten willst, wobei die Verarbeitung eines Objekts unabhängig von den anderen Objekten ist. Dann kannst du die Liste z.B. in zwei Teile zerlegen und die Verarbeitung in zwei Threads ausführen, wobei jeder Thread für jeweils eine Hälfte der Liste zuständig ist. Oder du viertels die Liste und hast vier Threads. Oder...
-Manche Rechenoperationen lassen sich gut zerlegen, z.B. Integrale. Es gibt z.B. eine Rechenvorschrift um Pi zu berechnen, wobei ein Integral über einen Viertelkreis in lauter schmale Streifen zerschnitten wird, wobei sich Pi umso genauer berechnen läßt je mehr Streifen man hat. Je mehr Streifen, desto mehr Threads kann man länger beschäftigen.
-Viele 3D- und Graphikberechnungen lassen sich auch gut parallelisieren. Graphikkarten sind im Prinzip nichts anderes als relativ langsame Prozessoren mit relativ wenigen Funktionen (soweit ich weiß meist keine Fließkommaberechnungen und ich weiß nichtmal ob überhaupt 64 Bit unterstützt werden, wenn ja dann war das jedenfalls sehr lange noch nicht so obwohl CPUs das schon jahrelang vorher konnten), dafür sind aber mehrere hundert bis mehrere tausend solcher Rechenkerne auf einem Chip drauf.

Zu deinem Problem: Ich sehe nicht was man da parallelisieren könnte. Du hast immer eine Bedingung, die erst erfüllt sein muß bevor du weitermachen kannst. Und du hast Wartezeiten...das kann man so auch nicht parallelisieren. Ich würde mir an deiner Stelle ein neues Problem suchen und zur Übung lösen. Vielleicht ist da was dabei:
Das sehe ich nicht so. Allerdings ist eine Synchronisation der einzelnen Vorgänge notwendig.
 
Das sehe ich nicht so.
Sehr informativ.

Mal ohne Sarkasmus: Was willst du denn parallelisieren? Theoretisch kann man natürlich zwei CoffeeMaker instanzieren und zwei einzelne Espressi parallel zubereiten und zu einem doppelten zusammenkippen. Aber ich bevorzuge da lieber die einfache Lösung, bzw. würde ein passendes Problem bevorzugen um eine Lösung zu üben. Ein einfaches Problem ins Unsinnige aufzubauschen – auch wenn es nur eine Übung ist – halte ich für fragwürdig.

Man kann natürlich auch jede Teilaufgabe in einen eigenen Thread auslagern und lauter Threads kaskadieren. Aber was hat man davon? Es wird vieles schlechter: Der Programmieraufwand steigt, und durch die Synchronisation steigt die Ausführungszeit und Ressourcenauslastung. Zwar minimal, zumindest in diesem Fall, aber eigentlich arbeitet man normalerweise ja mit mehreren Threads, um das Gegenteil zu erreichen. Und all diesen Nachteilen gegenüber sehe ich keinerlei Vorteil im gegebenen Beispiel.

Man kann natürlich immer noch der Meinung sein, daß es ja um eine Übung geht und ein praktischer Vorteil ohne jede Relevanz ist. Kann man machen, aber ich halte es trotzdem für schlechte Lehre und bleibe dabei, dem TS lieber ein Problem vorzustellen daß den Vorteil paralleler Programausführung auch darzustellen.
 
Am liebsten wäre es mir wenn das Kaffeekochen eine Weile dauert und das Konsumieren des Tasseninhaltes auch eine Weile.
Das ist die Anforderung. Natürlich kann man den Main-Thread warten lassen, so wie ich das verstehe ist das aber nicht das Ziel. Irgendeiner kocht Kaffee und irgendeiner trinkt Kaffee, wenn nun das Trinken länger dauert als das Kochen, dann muss der Consumer auf den Producer warten und umgedreht - wenn die Kanne voll ist, dann kann kein weiterer Kaffee gekocht werden.
Das sind also mind. ein Main-Thread, ein Consumer-Thread und ein Producer-Thread.
Es geht vermutlich nicht unbedingt um eine einfache Lösung, sondern um Übung und Skills - das schließe ich aus "Kaffee-Simulator" - denn diesen wird man vermutlich nicht in einem produktiven Umfeld benutzen wollen.
Der nächste Schritt ist dann vermutlich, es kommen weiter Kunden welche Kaffee möchten und andere Kunden gehen auch wieder...
 
Zuletzt bearbeitet:
Ich habe das eigt. so verstanden, daß der TS sich das selber beibringt und der Kaffeesimulator seine eigene Idee war. Und kein Lehrkonzept eines Erfahrenen (Studienaufgabe oder so), und da dementsprechend auch keine sinnvolle Erweiterung – sinnvoll im Sinne von hohem Erkenntnisgewinn – kommen wird.

Aber vielleicht schreibt der TS mal etwas dazu...
 
PS: Da fällt mir ein, mein erster echter Anwendungsfall für so eine Verarbeitungskette aus mehreren Threads war mein erster Anwendungsfall für Multithreading. Damals ging es darum, Daten von einem Gerät auszulesen und darzustellen, und ich habe die Kommunikation in zwei separaten Threads untergebracht.

Aber das würde ich trotzdem eher als Spezialfall einstufen.
 
Ich habe das eigt. so verstanden, daß der TS sich das selber beibringt und der Kaffeesimulator seine eigene Idee war. Und kein Lehrkonzept eines Erfahrenen (Studienaufgabe oder so), und da dementsprechend auch keine sinnvolle Erweiterung – sinnvoll im Sinne von hohem Erkenntnisgewinn – kommen wird.

Aber vielleicht schreibt der TS mal etwas dazu...
eigentlich ist die Idee, das Projekt nach und nach zu erweitern, es bleibt auch nicht eine Konsolen-App..
Grundkonzept soll sein Klassen zu erstellen die miteinander zusammen eine Simulation einer Office-Umgebung darstellen sollen. Also mit Personen die ihre Threads zum arbeiten, zum kaffee machen, zum kaffee trinken und z.b. toilettenpausen haben..

Erkenntnisgewinn ist dabei wie man die Klassen modelliert und miteinander verknüpft.
ich hab z.b. diesen Thread auch in einem anderen Forum gepostet und dort wurde ein anderer Ansatz für die Nutzung von Runnable vorgeschlagen. Dort hat ein User vorgeschlagen die Aktionen als separate Klassen zu modellieren. also sind dort Fill und Consume eigene Klassen die Runnable implementieren.
Hier im Forum wird aber die Klasse Coffemachine mit Runnable vorgeschlagen als Objekt das die Methode run hat. was mir ehrlich gesagt besser gefällt als die Methoden/Handlungen als separate Klassen zu erstellen..
 
Das ist die Anforderung. Natürlich kann man den Main-Thread warten lassen, so wie ich das verstehe ist das aber nicht das Ziel. Irgendeiner kocht Kaffee und irgendeiner trinkt Kaffee, wenn nun das Trinken länger dauert als das Kochen, dann muss der Consumer auf den Producer warten und umgedreht - wenn die Kanne voll ist, dann kann kein weiterer Kaffee gekocht werden.
Das sind also mind. ein Main-Thread, ein Consumer-Thread und ein Producer-Thread.
Es geht vermutlich nicht unbedingt um eine einfache Lösung, sondern um Übung und Skills - das schließe ich aus "Kaffee-Simulator" - denn diesen wird man vermutlich nicht in einem produktiven Umfeld benutzen wollen.
Der nächste Schritt ist dann vermutlich, es kommen weiter Kunden welche Kaffee möchten und andere Kunden gehen auch wieder...
genau, das sind die nächsten Schritte. mehrere Personen die Kaffee konsumieren und immer mal wieder die Kaffeemaschine anwerfen müssen wenn der Pot alle ist.
das ganze soll dann als GUI-Anwendung Elemente verwenden mit denen man das ganze darstellen kann, erstmal 2D. in 3D das wäre zu viel Mathe.
😀
danke für deine Vorschläge und Hinweise
 
Ah, da kommen wir der Sache also schonmal näher.

Dann würde ich dir, egal wie du das am Ende umsetzen willst, raten erstmal bei der reinen Konsolenanwendung zu bleiben. Für eine halbwegs brauchbare Visualisierung würden noch weitere Konzepte ins Spiel kommen (sowas wie MVC und Konsorten), und dann wird es rasch kompliziert.
Multithread-Systeme zu debuggen kann schnell sehr schwierig/aufwendig werden. Threadinterlocks sind ja noch auffällig wenn sie nur oft genug vorkommen, ein versautes Ressourcenmanagement (z.B. Variablen, in die mehrere Threads schreiben/von denen mehrere Threads lesen) dagegen kann einem mehr als nur einen Tag versauen.

Ich hatte mal ein kleines Programm geschrieben, das mit einem Stück Hardware kommunizieren sollte. Die Kommunikation bestand hardwareseitig aus einem SPI-Bus. SPI funktioniert folgendermaßen: Es gibt einen Master, und beliebig viele Slaves. Kommunikation findet dann – und nur dann – statt, wenn der Master den jeweiligen Slave dazu freigibt, wobei dann Senden und Empfangen gleichzeitig geschehen. Wenn der Master also wissen will ob der Slave was zu sagen hat, muß er selber etwas senden (auch wenn er nichts zu sagen hat).
Also ein Thread, der ständig bereits vorbereitete Bytes aus einer Queue sendet und empfangene Bytes in eine andere Queue reinschreibt. Da dieser Thread möglichst zeitgenau arbeitet und nicht mit schwer vorhersehbar viel Arbeit belastet wird: ein weiterer Thread, der den empfangenen Bytesalat in eine brauchbare Nachricht konvertiert und ein dritter Thread, der aus Nachrichten Bytesalat macht, der sendefähig ist und von der Hardware verstanden wird.

Ich habe bestimmt zwei oder drei Wochen nur damit verbracht, einen speziellen Test jeweils für alle drei Threads zu schreiben um herauszufinden, welcher Thread in einem Moment wo reingeschreiben/gelesen hat, wo er nicht reinschreiben/lesen durfte. Ich weiß nicht mehr genau warum, aber mit Reflexion mußte ich mich zum Debuggen ebenfalls beschäftigen. Ziemlich viele, ziemlich komplizierte Sachen für jemanden, der eigentlich kein Programmierer sein will. 😀
 
Du kannst mal hier: https://www.java-forum.org/thema/queue-remove-no-such-element-exception.203298/#post-1361693 schauen.
Es wird alle 3 Sekunden ein zufälliger Text erzeugt, dieser wird in eine Queue gepackt. Ein anderer Thread holt diesen zufälligen Text wieder aus der Queue heraus - braucht aber dafür 3 mal so lange. Also füllt sich die Queue immer weiter. Nach einer Minute stoppt das Füllen und der andere Thread läuft so lange, bis alle Texte aus der Queue heraus geholt wurden.
Das Szenario passt natürlich nicht ganz auf deinen Anwendungsfall, möglicherweise kannst du aber daraus etwas mitnehmen.
 

Neue Themen


Zurück
Oben