Generator mit virtuellem Thread

Barista

Top Contributor
Seltsamerweise findet man (fand ich) im Netz kaum Beispiele für Generatoren, die es in anderen Programmiersprachen gibt oder die man zum Beispiel in C# mit yield leicht realisieren kann.

Es geht darum, dass man mehrere Werte (Objekte) in einem Iterator zurück gibt, wobei der Iterator zwischen den next-Aufrufen den aktuellen Status vermerken und wieder aufnehmen muss. Man kann nicht einfach eine for-Schleife hinschreiben. Rekursiv wird es ganz übel (Iterator über Baum-Elemente).

Ich weiß nicht, ob es mit Streams eine bessere Lösung gibt.

Mit den neuen virtuellen Threads sollte dies einfach sein:

Java:
package kh.gen_virt_thrd;

import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.concurrent.LinkedBlockingDeque;

/**
 * Abstract class to generate values
 * with support of virtual thread.
 */
abstract public class GeneratorWthVrtThrd<T>
implements Iterable<T>
{
    /**
     * Method to implement to generate values.
     *
     * @param queue the output queue for the generated values
     */
    abstract public void generate(
            final GeneratorDeque<T> queue );

    @Override
    public final Iterator<T> iterator()
    {
        final GeneratorDeque<T> newQueue = new GeneratorDeque<T>();

        Thread.ofVirtual().start( () -> this.generate( newQueue ) );

        return new Iterator<T>()
        {
            private final GeneratorDeque<T> queue = newQueue;

            @Override
            public boolean hasNext()
            {
                return ! this.queue.isClosed();
            }

            @Override
            public T next()
            {
                if ( ! this.hasNext() )
                {
                    throw new NoSuchElementException();
                }

                return this.queue.take();
            }
        };
    }

    /**
     * Queue class to transfer generated values to consumer.
     */
    public static class GeneratorDeque<T>
    {
        private final LinkedBlockingDeque<T> innerQueue = new LinkedBlockingDeque<T>( 1 );

        private boolean isClosed;

        public void close()
        {
            this.isClosed = true;
        }

        public boolean isClosed()
        {
            return this.isClosed;
        }

        /**
         * Put value in queue.
         * Blocks if queue has no space for specified value.
         *
         * @param e value to put
         */
        public void put(
                final T e )
        {
            if ( this.isClosed )
            {
                throw new IllegalStateException( "queue already closed" );
            }

            try
            {
                this.innerQueue.put( e );
                // bei der Variante mit GeneratorDeque als top level Klasse war dieses sleep notwendig
                //Thread.sleep( 1 );
            }
            catch ( final InterruptedException exc )
            {
                throw new RuntimeException( exc );
            }
        }

        /**
         * Takes value from queue.
         * Blocks if queue has no value.
         *
         * @return value from queue
         */
        public T take()
        {
            if ( this.isClosed )
            {
                throw new IllegalStateException( "queue already closed" );
            }

            try
            {
                return this.innerQueue.take();
            }
            catch ( final InterruptedException exc )
            {
                throw new RuntimeException( exc );
            }
        }
    }

}

Hier der Code in Anwendung:

Java:
package kh.gen_virt_thrd;

/**
 * Spike for {@link GeneratorWthVrtThrd}.
 */
public class Integer0to2Generator
extends GeneratorWthVrtThrd<Integer>
{
    /**
     * @param args
     */
    public static void main(final String[] args)
    {
        // use output buffer to avoid blocking main thread for io operations
        final StringBuilder buff = new StringBuilder();

        for ( final Integer i : new Integer0to2Generator() )
        {
            //System.out.println( i );
            buff.append( i );
            buff.append( '\n' );
        }

        System.out.println( buff );
    }

    @Override
    public void generate(
            final GeneratorDeque<Integer> queue )
    {
        for ( int i = 0 ; i < 3 ; i++ )
        {
            //System.out.println( "queue.put: " + i );
            queue.put( i );
            //Thread.sleep( 1 );
        }
        //System.out.println( "queue.close" );
        queue.close();
    }

}

Erst hatte ich die static encapsulated Klasse GeneratorDeque als eigene top level Klasse.

Da hat der Code nur funktioniert, wenn ich das Thread.sleep( 1 ) aufgerufen habe (mit Thread.sleep( 0 ) ) hat es auch nicht funktioniert).

Der main-Thread hat blockiert, Es gab nur die Ausgaben 0, 1 und dann nix mehr, aber das Programm lief noch.

Komischerweise musste ich Thread.sleep( 1 ) nicht mehr aufrufen, als ich die Klasse GeneratorDeque zur eingeschlossenen Klasse gemacht habe.

Ich bin mir jetzt unsicher, ob das Konstrukt nicht doch irgendwann ungewollt blockiert.

Die jetzt auskommentierten System.out.println haben den Code auch funktionieren lassen, klar, bei IO blockiert der virtuelle Thread, gibt also die Kontrolle ab.

Wie kann ich es wirklich richtig machen?
 

Oneixee5

Top Contributor
Wenn ich das richtig sehe willst du erreichen, dass deine sequentielle Eingabe (put) - zwar in einem anderen Thread - trotzdem wieder sequentiell abgearbeitet wird. Das ganze in einem virtuellen Thread. Nun ist es so, das hier ein Thread eigentlich gar nicht notwendig wäre aber die willst einen non-blocking-input erreichen. Die Ausgabe soll dann im Main-Thread erfolgen.
Virtuelle Threads sind für die kurzzeitige Nutzung mit einer konkreten kompakten Aufgabe gedacht. Es werden direkt Vorteile der CPU genutzt und es entsteht minimaler Overhead. Um sich an diese Konzept zu halten, solltest du bei jedem put statt der Queue jedes Mal einen neuen virtuellen Thread erzeugen. Allerdings ist dann nicht garantiert, dass die Ausgabe in der Reihenfolge der Eingabe erfolgt.

Zur Funktion: LinkedBlockingDeque implementiert bereits Iterable, von daher ist GeneratorDeque eigentlich überflüssig. Für die Funktonalität queue.close(); sehe ich keine Notwendigkeit. Der Ausgabe-Thread ist hier der Main-Thread. Dieser wird also blockiert, wenn keine Eingaben in der Queue vorhanden sind. Das sehe ich problematisch, denn damit blockiert man diesen. Nur deshalb benötigst du close(); Wäre es nicht besser, wenn die Ausgabe in einem eigenem Thread ablaufen würde? Damit wäre der Main-Thread nie blockiert.

Ich denke die Struktur ist hier falsch gewählt. Es sollte natürlich eine BlockingQueue geben aber eingaben sollten von mind. einem Producer in der Queue abgelegt werden und dann von einem oder mehr Consumern der Queue wieder entnommen werden. Das Ende des Programms wird dadurch ermöglicht, dass auf allen noch laufenden Producer-Threads und Consumer-Threads interrupted aufgerufen wird. Dabei ist es praktisch, wenn man für die Erzeugung Threadgroup verwendet, so behält man die Kontrolle über alle eigene laufenden Threads.
 

Barista

Top Contributor
Vielen Dank für Deine Antwort.

Ich war eine zeitlang offline (Familie).

Virtuelle Threads sind für die kurzzeitige Nutzung mit einer konkreten kompakten Aufgabe gedacht.

Es geht nicht um kompakt, sondern um ein Concurrency-Programmiermodell, also das erhalten des Status über mehrere Aufrufe (meist callbacks).

LinkedBlockingDeque implementiert bereits Iterable
Ich will die Queue nicht mit allen Daten füllen, sondern die Queue dient nur zur Thread-Kommunikation (Vergleiche Go-Channel).
Das Ende des Programms wird dadurch ermöglicht, dass auf allen noch laufenden Producer-Threads und Consumer-Threads interrupted aufgerufen wird.
Echte Threads will ich vermeiden, deshalb gibt es virtuelle Threads.
 

httpdigest

Top Contributor
Du hast vorhin den Vergleich zu Go-Channels und den Go-Routinen gemacht. Bei Go-Channels ist es so, dass die Schreib- (<-) bzw. Lese- (->) Operation auf Channels blockiert, wenn der Channel-Puffer voll ist, und das ist der Punkt, wann die gerade ausgeführte Go-Routine "geparkt" wird und eine andere Go-Routine vom Thread ausgeführt wird.

Genauso funktionieren JVM VirtualThreads auch: Das ganze JRE ist quasi "umgeschrieben" worden, so dass alle Operationen, die "blockieren" würden, und im Kontext eines VirtualThreads ausgeführt werden, eben nicht wirklich den echten Thread blockieren, sondern den VirtualThread nur "parken". Danach führt der echte Thread andere geparkte VirtualThreads aus, wenn sie wieder bereit sind, ausgeführt zu werden.

Um eine Koordination zwischen zwei VirtualThreads zu machen, brauchst du also eine blockierende Operation, also z.B. (wie ja bereits erwähnt wurde), eine LinkedBlockingDeque.

Beispiel in Go:
Code:
c := make(chan int)
go func() {
  for i := 0; i < 1000; i++ {
    c <- i // <-- blockiert!
  }
  close(c)
}()
for n := range c { // <- blockiert!
  fmt.Println(n)
}

Das Schreiben in der Go-Routine sowieo das Lesen (implizit durch den "range" Operator) blockieren.
 

Barista

Top Contributor
Vielen Dank für alle Antworten.

Ich habe meinen Code noch mal überarbeitet.

Erstens habe ich das Feld isClosed mit in den threadsafe-Bereich hereingenommen, das war vorher sicher falsch.

Dann habe ich im Netz gefunden, dass man zum Blockieren virtueller Threads ReentrantLock verwenden soll.

Ich habe mir ein bisschen Code aus LinkedBlockingDeque geklaut.

Neben dem java.util.concurrent.locks.ReentrantLock ist noch java.util.concurrent.locks.Condition wichtig.

In diesem Video sieht man übrigens, dass mit JEP 444 auch die alte synchronized-wait-Notation für virtuelle Threads fit gemacht werden soll (eigentlich müsste dort synchronized-wait-notify stehen):
(siehe bei 12:00).

Mein Code funktioniert jetzt, wobei ich nicht beweisen kann, dass er wirklich 100prozentig korrekt ist.

Ein bisschen ärgerlich ist, dass es mit den auskommentierten Blöcken für isClosed nicht funktioniert.
Dafür müsste man doch eine LinkedList mit dem Pattern Giftpille verwenden.
Zu dem Pattern Giftpille siehe das Buch von Brian Goetz https://shop.studibuch.de/9780321349606/java-concurrency-in-practice.

Java:
package kh.gen_virt_thrd;

import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Abstract class to generate values
 * with support of virtual thread.
 */
abstract public class VrtThrdGenerator<T>
implements Iterable<T>
{
    /**
     * Method to implement to generate values.
     *
     * @param queue the output queue for the generated values
     */
    abstract public void generate(
            final GeneratorDeque<T> queue );

    @Override
    public final Iterator<T> iterator()
    {
        final GeneratorDeque<T> queue = new GeneratorDeque<T>();

        Thread.ofVirtual().start( () -> this.generate( queue ) );

        return new Iterator<T>()
        {
            @Override
            public boolean hasNext()
            {
                return ! queue.isClosed();
            }

            @Override
            public T next()
            {
                if ( ! this.hasNext() )
                {
                    throw new NoSuchElementException();
                }

                return queue.take();
            }
        };
    }

    /**
     * Queue class to transfer generated values to consumer.
     */
    public static class GeneratorDeque<T>
    {
        final ReentrantLock lock = new ReentrantLock();

        /** Condition for waiting puts */
        private final Condition isValueThereCondition = this.lock.newCondition();

        /**
         * Queued value.
         *
         * volatile for extra thread safety.
         */
        private volatile T queuedValue;

        /**
         * Boolean value for is queued value there.
         *
         * volatile for extra thread safety.
         */
        private volatile boolean isValueThere;

        /**
         * Boolean value for closed queue.
         * Signals end of iteration.
         *
         * volatile for extra thread safety.
         */
        private volatile boolean isClosed;

        public void close()
        {
            // copy paste code from java.util.concurrent.LinkedBlockingDeque.putLast(E)
            final ReentrantLock lock = this.lock;
            lock.lock();
            try
            {
                this.isClosed = true;
                this.isValueThereCondition.signal();
            }
            finally
            {
                lock.unlock();
            }
        }

        public boolean isClosed()
        {
            // copy paste code from java.util.concurrent.LinkedBlockingDeque.putLast(E)
            final ReentrantLock lock = this.lock;
            lock.lock();
            try
            {
                return this.isClosed;
            }
            finally
            {
                lock.unlock();
            }
        }

        /**
         * Put value in queue.
         * Blocks if queue has no space for specified value.
         *
         * @param e value to put
         * @see java.util.concurrent.BlockingDeque.put(E)
         */
        public void put(
                final T e )
        {
            // copy paste code from java.util.concurrent.LinkedBlockingDeque.putLast(E)
            final ReentrantLock lock = this.lock;
            lock.lock();
            try
            {
                if ( this.isClosed )
                {
                    throw new IllegalStateException( "queue already closed" );
                }

                while ( this.isValueThere )
                {
                    this.isValueThereCondition.await();
                }

                if ( this.isClosed )
                {
                    throw new IllegalStateException( "queue already closed" );
                }

                this.queuedValue = e;
                this.isValueThere = true;
                this.isValueThereCondition.signal();
            }
            catch ( final InterruptedException iexc )
            {
                //System.err.println(e.getMessage());
                //e.printStackTrace();
                throw new RuntimeException( iexc );
            }
            finally
            {
                lock.unlock();
            }
        }

        /**
         * Takes value from queue.
         * Blocks if queue has no value.
         *
         * @return value from queue
         * @see java.util.concurrent.BlockingDeque.take()
         */
        public T take() // throws InterruptedException
        {
            // copy paste code from java.util.concurrent.LinkedBlockingDeque.putLast(E)
            final ReentrantLock lock = this.lock;
            lock.lock();
            try
            {
                // wrong code, because closing queue after put is not blocking
                //if ( this.isClosed )
                //{
                //    throw new IllegalStateException( "queue already closed" );
                //}

                while ( ! this.isValueThere )
                {
                    // wrong code, because closing queue after put is not blocking
                    //if ( this.isClosed )
                    //{
                    //    throw new IllegalStateException( "queue already closed" );
                    //}

                    this.isValueThereCondition.await();
                }

                final T retVal = this.queuedValue;
                this.isValueThere = false;

                this.isValueThereCondition.signal();

                return retVal;
            }
            catch ( final InterruptedException iexc )
            {
                //System.err.println(e.getMessage());
                //e.printStackTrace();
                throw new RuntimeException( iexc );
            }
            finally
            {
                lock.unlock();
            }
        }
    }

}

Hier ein Test:

Java:
package kh.gen_virt_thrd;

/**
 * Spike for {@link VrtThrdGenerator}.
 */
public class Integer0to2Generator
extends VrtThrdGenerator<Integer>
{
    /**
     * Main method for test.
     * @param args unused
     */
    public static void main(final String[] args)
    {
        // use output buffer to avoid blocking main thread for input-output operations
        final StringBuilder buff = new StringBuilder();

        for ( final Integer i : new Integer0to2Generator() )
        {
            //System.out.println( i );
            buff.append( i );
            buff.append( '\n' );
        }

        System.out.println( buff );
    }

    @Override
    public void generate(
            final GeneratorDeque<Integer> queue )
    {
        for ( int i = 0 ; i < 3 ; i++ )
        {
            //System.out.println( "queue.put: " + i );
            queue.put( i );
            //Thread.sleep( 1 );
        }
        //System.out.println( "queue.close" );
        queue.close();
    }

}

Und ein etwas umfangreicheres Beispiel, damit man den Vorteil des Generators erkennt:
Java:
package kh.gen_virt_thrd;

/**
 * Spike for {@link VrtThrdGenerator}.
 */
public class PlaceValueGenerator
extends VrtThrdGenerator<String>
{
    /**
     * Main method for test.
     * @param args unused
     */
    public static void main(final String[] args)
    {
        // use output buffer to avoid blocking main thread for input-output operations
        final StringBuilder buff = new StringBuilder();

        for ( final String str : new PlaceValueGenerator() )
        {
            buff.append( str );
            buff.append( '\n' );
        }

        System.out.println( buff );
    }

    @Override
    public void generate(
            final GeneratorDeque<String> queue )
    {
        for ( int i = 0 ; i < 3 ; i++ )
        {
            this.subGenerate(
                    queue ,
                    String.valueOf( i ) );
        }
        queue.close();
    }

    private void subGenerate(
            final GeneratorDeque<String> queue ,
            final String prefix )
    {
        for ( int i = 0 ; i < 3 ; i++ )
        {
            queue.put( prefix + i );
        }
    }
}
 
Zuletzt bearbeitet:

Barista

Top Contributor
Wieso will man eigentlich einen Paralleliterator haben und was soll das Ergebnis sein?
Von parallel ist keine Rede.

Versuch mal einen Iterator zu schreiben, zum Beispiel ein Iterator der alle geraden Zahlen ab 0 liefert.

Du musst die beiden Methoden hasNext und next implementieren.

Nach jedem Aufruf musst Du die letzte oder nächste Zahl vermerken, erhöhen und zurück geben.

Wenn der ursprüngliche Algorithmus geschachtelte Schleifen oder Methodenaufrufe (vielleicht rekursiv) enthält, wird das Vermerken ziemlich schwierig.

Der Wunsch ist, dass man den Code hinschreiben kann, als würde man im Erzeuger-Programm bleiben (ohne Unterbrechung und Wiederaufnehmen). Das wäre dann blockierende Schreibweise (Notation).

Es gibt sowas unter dem Namen Continuations, wenn Du das Nachlesen willst, aber nicht in Java.

In C# gibt es yield, mit dem man dies erreichen kann.

In irgendwelchen Sprachen gibt es auch Möglichkeiten zum Formulieren von Generatoren, weiß jetzt nicht wie genau.
 

KonradN

Super-Moderator
Mitarbeiter
Ich bin mir nicht sicher, was die genauen Anforderungen sein sollen. Aber ist das nicht der typische Fall für funktionale Interfaces wie Consumer und Supplier?

Du kannst also einfach einen Supplier haben. Falls das get() nicht reicht, dann hast Du halt auch gerne einen Iterator mit den zwei Methoden, aber das ist nicht notwendig.
Wenn der ursprüngliche Algorithmus geschachtelte Schleifen oder Methodenaufrufe (vielleicht rekursiv) enthält, wird das Vermerken ziemlich schwierig.
Was ist denn genau die Schwierigkeit? Du kannst das get() synchronisieren und dann hast Du eine klare zeitliche Abfolge. Komplexer kann es werden, wenn Du es in mehreren Threads haben willst. Aber auch da gibt es dann Klassen, die man verwenden kann so dass Thread z.B. eine Queue füllen und get() dann nur ein Element heraus nimmt.

Der Supplier ist dann auch direkt verwendbar in z.B. Streams: Stream.generate wäre hier eine Möglichkeit, die verwendet werden könnte.

Und die Verarbeitung kann z.B. auch direkt über einen Consumer gehen. Das kann bei einer Nutzung von Stream<T> ein einfaches forEach(consumer) sein.

Generell ist hier also der Gedanke: Teile und Herrsche. Und nutze die typischen Verfahren, die die Umgebung nutzt. Und das könnten hier aus meiner Sicht halt Supplier<T>, Consumer<T>, Stream<T> sein.

Und die Frage ist dann tatsächlich: Was für einen Iterator muss man wirklich noch selbst schreiben? Sicher, dass die Collection Klassen vom Java Framework da nicht vieles bereits abdecken und gezielt genutzt werden können? Mir scheint das Kernproblem etwas zu sein, dass Du Konstrukte aus anderen Sprachen Java aufzwingen möchtest. Aber evtl. ist der Eindruck auch komplett falsch und ich schreibe hier gerade am Thema komplett vorbei. Sollte das der Fall sein, würde ich um Verzeihung bitten.
 

Barista

Top Contributor
Was ist denn genau die Schwierigkeit? Du kannst das get() synchronisieren und dann hast Du eine klare zeitliche Abfolge. Komplexer kann es werden, wenn Du es in mehreren Threads haben willst. Aber auch da gibt es dann Klassen, die man verwenden kann so dass Thread z.B. eine Queue füllen und get() dann nur ein Element heraus nimmt.
Threads sind eine Notlösung für die fehlende Möglichkeit Generatoren/Continuations in Java und auch schwergewichtig, nur begrenzt nutzbar.
 

Barista

Top Contributor
Und die Frage ist dann tatsächlich: Was für einen Iterator muss man wirklich noch selbst schreiben? Sicher, dass die Collection Klassen vom Java Framework da nicht vieles bereits abdecken und gezielt genutzt werden können?
Bei Iteratoren geht es Algorithmen, bei Collections um Daten.
Fülle doch mal die Collection mit allen natürlichen Zahlen, der Speicher wird teuer.

Ich muss mal sagen, dass mich Dein Posting und auch ein weiteres vorheriges von jemand Anderem etwas nervt.

Respekt vor Deinem Wissen, da kann ich nicht mithalten. Und auch Respekt für das, was Du hier leistest.

Aber in diesem Fall ist es nicht passend.
 

KonradN

Super-Moderator
Mitarbeiter
Threads sind eine Notlösung für die fehlende Möglichkeit Generatoren/Continuations in Java und auch schwergewichtig, nur begrenzt nutzbar.
Die Idee von Java waren halt die VirtuellenThreads. Damit hat man eine leichtgewichtige Thread Lösung, die man verwenden kann. Die Java Entwickler haben sich hier explizit gegen Konstrukte anderer Sprachen entschieden. Es hat zwar etwas länger gedauert, die fertig zu stellen und für die Produktion freizugeben, aber das ist ja zum Glück Vergangenheit.

Bei Iteratoren geht es Algorithmen, bei Collections um Daten.
Fülle doch mal die Collection mit allen natürlichen Zahlen, der Speicher wird teuer.

Ich muss mal sagen, dass mich Dein Posting und auch ein weiteres vorheriges von jemand Anderem etwas nervt.

Respekt vor Deinem Wissen, da kann ich nicht mithalten. Und auch Respekt für das, was Du hier leistest.

Aber in diesem Fall ist es nicht passend.
Das ist ok. Ich kann akzeptieren, dass ich Dir nicht weiter helfen kann. Aber was Du hier als Probleme beschreibst sind doch eher Trivialitäten.

Collections speichern Daten, aber natürlich kannst Du doch den Thread, der Elemente generiert, stoppen, falls n Elemente in der Queue sind und wieder weiter laufen lassen, wenn der Füllstand der Queue auf eine andere Marke kommt. Das ist sogar ganz trivial aufbaubar, wenn man die Grenzen auf 0/1 setzt. Dann braucht es nicht einmal eine entsprechende Queue sondern nur ein nextElement.

Ist wie Iterator, nur ohne hasNext.
Ich habe nichts anderes behauptet…


Aber ich muss Dich nicht weiter nerven, daher wünsche ich Dir viel Glück mit Deiner Problematik.
 

Barista

Top Contributor
Muss ich?
Java:
Iterator<Integer> it = IntStream.rangeClosed(0, Integer.MAX_VALUE / 2).map(z -> z * 2).iterator();

Klasse, diese Möglichkeit kannte ich nicht.

Aber Du solltest schon zugeben, dass es sich bei den geraden Zahlen um ein extrem einfaches Beispiel handelt.

Der Generator ist für komplexere Probleme gedacht.

Algorithmen, die Methodenaufrufe, möglicherweise rekursiv, erfordern.
 

Barista

Top Contributor
Nichts erfordert das etwas rekursiv ist. Es ist eine Gesetzmäßigkeit, dass sich jede Rekursion als Iteration darstellen lässt und auch anders herum.

Wieso gibt es yield nicht in Java? Steht doch in der API und funktioniert auch.
Dieses Posting hat was mit "Mit .parallel() kann man die Aufgabe auch auf mehrere Threads verteilen. Es hat irgendwie etwas den Anschein, als sollte ein Problem gefunden werden." zu tun?

Selbstverständlich kann man ohne Rekursion und yield auskommen, ist aber nicht so bequem.
 

KonradN

Super-Moderator
Mitarbeiter
Aber Du solltest schon zugeben, dass es sich bei den geraden Zahlen um ein extrem einfaches Beispiel handelt.
Und Java Streams sind extrem vielseitig und nicht auf int Zahlen oder so beschränkt. Wenn Du DIch mit Hinweisen mehr im Detail beschäftigen würdest, dann hättest Du auch z.B. einen Stream<T> von einem Supplier<T> gebaut.

Ja super - ein Weg, einen Iterator zu bauen. Dafür gibt es dann ein eigenes C# Schlüsselwort. Java hat das mit Java 8 mit Streams gelöst, hat dann im Framework Möglichkeiten aufgebaut wie z.B. schon die Möglichkeit per Supplier<T> und Stream.generate dann Streams zu erzeugen...

Selbstverständlich kann man ohne Rekursion und yield auskommen, ist aber nicht so bequem.
Nein, das ist nicht komplexer. Du hast halt dann ein Supplier<T>, das eine Methode hat, die den nächsten Wert zurück gibt. Das erzeugt dann auch keinen Code, der komplexer ist, wie z.B. das yield Beispiel...

Mein (unerwünschter) Ratschlag:
Beschäftige Dich doch einmal im Detail mit Java und den Möglichkeiten von Java. Und wenn Du ein konkretes Problem siehst, dann beschreibe es anständig. Dann kann man auch Möglichkeiten aufzeigen. Aufzeigen, wie man etwas unterteilen kann u.s.w.
Und wenn Du ein Problem hast: Dann schau richtig in die Doku! Thread.suspend / Thread.resume wurden mit Java 1.2 deprecated! Die Doku verlinkt eine ganze Seite, die das wieso erläutert und auch auch sleep und so hinweist. Davon abgesehen gibt es deutlich mehr Möglichkeiten (Object.wait / Object.notify(All); Semaphore; Future.get(); CountDownLatch; CyclicBarrier; ....) und man sollte schon im Detail wissen, was man genau braucht / will. Und da sind wir noch bei den absoluten Basics der Thread verwaltung - aber es gibt ja auch fertige Lösungen wie ThreadPools und so (was auch bei parallelen Streams verwendet werden dürfte).

Aber was verschwende ich meine Zeit mit unerwünschten Ratschlägen - All meine Aussagen ändern nichts daran, dass Java kein yield hat und auch in Zukunft wohl nie ein yield bekommen wird. Vielleicht ist die Antwort daher auch einfach: Java ist für Dich schlicht ungeeignet. Bleib einfach bei C#.
 

mihe7

Top Contributor
Wieso gibt es yield nicht in Java? Steht doch in der API und funktioniert auch.
Wo steht das?

Aber Du solltest schon zugeben, dass es sich bei den geraden Zahlen um ein extrem einfaches Beispiel handelt.
Klar, das war auch nur als kleiner Scherz gedacht, weil man hier die Methoden nicht selbst implementieren muss :) Darum auch der Umweg über rangeClosed und MAX_INTEGER/2, ansonsten hätte ich z. B. auch einfach schreiben können
Java:
Iterator<Integer> it = IntStream.iterate(0, i -> i + 2).iterator();
Hier ist i -> i + 2 das Pendant zur next()-Methode und hasNext() liefert immer true.

Zum yield schreibe ich nachher noch was, wenn ich Zeit habe.
 

Barista

Top Contributor
Und wenn Du ein Problem hast: Dann schau richtig in die Doku! Thread.suspend / Thread.resume wurden mit Java 1.2 deprecated! Die Doku verlinkt eine ganze Seite, die das wieso erläutert und auch auch sleep und so hinweist. Davon abgesehen gibt es deutlich mehr Möglichkeiten (Object.wait / Object.notify(All); Semaphore; Future.get(); CountDownLatch; CyclicBarrier; ....) und man sollte schon im Detail wissen, was man genau braucht / will. Und da sind wir noch bei den absoluten Basics der Thread verwaltung - aber es gibt ja auch fertige Lösungen wie ThreadPools und so (was auch bei parallelen Streams verwendet werden dürfte).
Ich habe eigentlich schon mehrmals geschrieben, dass ich virtuelle Threads nur eine Ersatzlösung für das fehlende yield verwenden will. Ich will kein echtes Multithreading machen.
Möglicherweise muss man (ich) sich dann mit den Feinheiten des Multithreading auseinandersetzen, aber nicht weil man (ich) Multithreading machen will.
 

LimDul

Top Contributor
Ich verstehe das Problem ehrlich gesagt nicht, warum man überhaupt virtuelle Threads braucht? Du hast einen Generator der Werte zurückgibt. Was braucht man da Threads?
 

Barista

Top Contributor
Aber was verschwende ich meine Zeit mit unerwünschten Ratschlägen - All meine Aussagen ändern nichts daran, dass Java kein yield hat und auch in Zukunft wohl nie ein yield bekommen wird. Vielleicht ist die Antwort daher auch einfach: Java ist für Dich schlicht ungeeignet. Bleib einfach bei C#.
Ich verstehe Dich so: "Weil es in Java kein yield gibt, soll man sich nicht damit befassen, wie man etwas vergleichbares realisieren kann".

Also soll man sich nie wünschen, etwas zu realisieren, was es noch nicht gibt.

Ok, dann gäbe es keine Computer, keine Technik, keinen Wohlstand... usw.

Es ist relativ normal für Programmierer, dass Lösungen in Libs oder Frameworks gegossen werden.

Keine Ahnung, warum Du ein Problem daraus machst.

Dann den Fragesteller/Diskussionsbeginner erst mal als unfähig hinzustellen und das Thema nicht zu verstehen (bzw. nicht verstehen zu wollen) und demjeniegen dann zu unterstellen, er würde seine eigene Aussage nicht verstehen, ist schon ziemlich übel.
 

Barista

Top Contributor
Ich verstehe das Problem ehrlich gesagt nicht, warum man überhaupt virtuelle Threads braucht? Du hast einen Generator der Werte zurückgibt. Was braucht man da Threads?
Ich habe das in diesen beiden Diskussionen schon mehrmals geschrieben. Guck mal da.
Respekt, dass Du zugibst, das Thema nicht zu verstehen.
Andere Leute fangen einfach mit Mobbing an.
 

LimDul

Top Contributor
Nein, ich sehe in deinen Erklärungen nur "Ich möchte virtuelle Threads verwenden". Warum nicht single-Threaded? Warum kann ein Aufruf von next nicht synchron stattfinden? Warum muss da ein Thread hinterstecken?
 

mihe7

Top Contributor
Ich verstehe das Problem ehrlich gesagt nicht, warum man überhaupt virtuelle Threads braucht? Du hast einen Generator der Werte zurückgibt. Was braucht man da Threads?
Wenn ich es richtig verstehe, geht es darum, dass yield return zu einem pause/resume führt, so dass Anweisungen, die nach einem yield return kommen, auch erst nach der Behandlung des generierten Wertes ausgeführt werden.

Mal von der Microsoft-Seite (https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/yield):
C#:
var numbers = ProduceEvenNumbers(5);
Console.WriteLine("Caller: about to iterate.");
foreach (int i in numbers)
{
    Console.WriteLine($"Caller: {i}");
}

IEnumerable<int> ProduceEvenNumbers(int upto)
{
    Console.WriteLine("Iterator: start.");
    for (int i = 0; i <= upto; i += 2)
    {
        Console.WriteLine($"Iterator: about to yield {i}");
        yield return i;
        Console.WriteLine($"Iterator: yielded {i}");
    }
    Console.WriteLine("Iterator: end.");
}

Liefert als Ausgabe:
Code:
// Output:
// Caller: about to iterate.
// Iterator: start.
// Iterator: about to yield 0
// Caller: 0
// Iterator: yielded 0
// Iterator: about to yield 2
// Caller: 2
// Iterator: yielded 2
// Iterator: about to yield 4
// Caller: 4
// Iterator: yielded 4
// Iterator: end.

EDIT: das lässt sich natürlich single threaded in Java realisieren, der Code dürfte allerdings nicht besonders schön werden.
 

LimDul

Top Contributor
Ja, das ist aber C#. Sowas gibt es in Java nicht. Ich kann natürlich versuchen ein technisches Konstrukt nachbauen - aber ich verstehe die Anforderung nicht, welches Problem gelöst werden soll.

Entweder kann ich next synchron und blockierend machen oder ich mach es als Thread im Hintergrund komplett unabhängig von der Abfrage.
 

Barista

Top Contributor
Wenn ich es richtig verstehe, geht es darum, dass yield return zu einem pause/resume führt, so dass Anweisungen, die nach einem yield return kommen, auch erst nach der Behandlung des generierten Wertes ausgeführt werden.
Ja.
EDIT: das lässt sich natürlich single threaded in Java realisieren, der Code dürfte allerdings nicht besonders schön werden.
Ja.

Wer es gebildet ausdrücken will, kann es Continuations nennen.
 

KonradN

Super-Moderator
Mitarbeiter
Ich verstehe Dich so: "Weil es in Java kein yield gibt, soll man sich nicht damit befassen, wie man etwas vergleichbares realisieren kann".
Ja, das zeigt deutlich, dass Du ganz offensichtlich nicht einmal im Ansatz versuchst, Antworten zu verstehen. Du reitest immer nur auf dem yield herum anstatt wirklich die genauen Anforderungen und Probleme zu beschreiben. Und dann kommen Dinge auf den Tisch wie Threads die Du haben willst, für die einfach keine Anforderung sichtbar ist.

Mag sein, dass Du meinst, dass Du da irgendwo schon etwas zu geschrieben hast. Aber anscheinend wurde das nicht verstanden. Und da solltest Du dann doch ein Interesse daran haben, es zu erläutern. Statt dessen kommt aber nur Antworten a.la. "nicht hilfreiche Antworten" und die, die sich die Zeit nehmen, dir zu antworten, werden angegangen.

Wenn ich es richtig verstehe, geht es darum, dass yield return zu einem pause/resume führt, so dass Anweisungen, die nach einem yield return kommen, auch erst nach der Behandlung des generierten Wertes ausgeführt werden.

Mal von der Microsoft-Seite (https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/yield):
C#:
var numbers = ProduceEvenNumbers(5);
Console.WriteLine("Caller: about to iterate.");
foreach (int i in numbers)
{
    Console.WriteLine($"Caller: {i}");
}

IEnumerable<int> ProduceEvenNumbers(int upto)
{
    Console.WriteLine("Iterator: start.");
    for (int i = 0; i <= upto; i += 2)
    {
        Console.WriteLine($"Iterator: about to yield {i}");
        yield return i;
        Console.WriteLine($"Iterator: yielded {i}");
    }
    Console.WriteLine("Iterator: end.");
}

Liefert als Ausgabe:
Code:
// Output:
// Caller: about to iterate.
// Iterator: start.
// Iterator: about to yield 0
// Caller: 0
// Iterator: yielded 0
// Iterator: about to yield 2
// Caller: 2
// Iterator: yielded 2
// Iterator: about to yield 4
// Caller: 4
// Iterator: yielded 4
// Iterator: end.

EDIT: das lässt sich natürlich single threaded in Java realisieren, der Code dürfte allerdings nicht besonders schön werden.
Wo ist denn da das Problem? Das mit dem ProduceEvenNumbers ist dann ein einfacher Supplier<Integer>. Das Limit upTo wandert aber in den Stream (so man Streams verwenden möchte) was auch Sinn macht, denn der Generator ist ein generisches erzeugen der geraden Zahlen und das Limitieren ist eine Frage der Nutzung.

Das ist übrigens eine Thematik, bei de viele am Anfang Probleme haben: Erkennen, wo was hin gehört. Wie teilt man eine Problematik auf. Da hätte man hier in den Threads durch konkrete Szenarien bestimmt sehr viel recht schön aufbauen können. Also selbst wenn man eine Anforderung hat, bei der ein Generator mit Threads Elemente erzeugen soll, bedeutet das ja nicht, dass der Generator selbst das verwalten muss. So wie man das Supplier<T> Interface nutzen kann um dann auf einfache Art und Weise einen Stream / Iterator / ... bekommen zu können (Wer schreibst komplett seinen eigenen Iterator von Grund auf?) kann man hier dann auch Ansätze finden. Und schon spielt das Thema des anderen Threads keine Rolle mehr: Generator hat dann einfach keinen Thread der blockieren wird.

Erst einmal der Thread Ansatz: Es ist unsinnig, einen Aufbau zu haben wie:
  • Ein Thread beantragt etwas von einem Generator. Das blockiert dann diesen Thread.
  • Der Generator generiert etwas auf dem eigenen Thread, gibt das Ergebnis zurück und blockiert dann den eigenen Thread
  • Der ursprüngliche Thread bekommt die Antwort und entsperrt damit wieder

Das macht doch nicht viel Sinn, oder?

Wenn man parallele Abarbeitungen haben möchte, dann machen Threads Sinn. Aber auch das muss optimiert sein. Es macht keinen Sinn, einfach massiv Threads zu erzeugen. Mit Anzahl der Threads steigt die Performance aber ab einer bestimmten Anzahl sinkt die Performance wieder. Ist ja auch klar: Man hat in der Hardware x parallele Ausführungen und alles darüber senkt die Performance durch die Umschaltungen. Das ist bei virtuellen Threads massiv reduziert aber die Kernproblematik bleibt. Das ist mit ein Grund für ThreadPools und so: Es wird halt minimiert auf ein mögliches Optimum. Dann wird ein paralleler Stream halt nur maximal x Threads parallel haben, weil bei einem Thread mehr die Performance sinken würde.

Und das zeigt auch, dass es Sinn macht, sowas zentral zu haben. Die Generatoren geben also Tasks zur Ausführung in eine Queue und die werden dann abgearbeitet (Jetzt bitte nicht wörtlich nehmen! Queue meint hier nur eine zentrale Verwaltung von Aufgaben). Und das findet sich dann in den Frameworks auch wieder. Lowlevel hat man dann z.B. Interfaces wie ExecutorService und Implementationen wie ThreadPoolExecuter oder in Frameworks finden sich dann High Level Lösungen wie @Async in Spring Boot und ähnliches ...

Vielleicht hast Du ja Lust und Zeit einmal zu erläutern, was da aus Deiner Sicht "nicht besonders schön" würde. Das könnte eine interessante Sache sein (und eine reizvolle Refactoring Übung).
 

Barista

Top Contributor
Zum Allgemeinen (der Ton macht die Musik)

Letztens fragte hier jemand wie er es folgendes lösen könne, er wolle ein paar Daten eines Programmes (muss man jetzt wahrscheinlich App nennen) in einem RandomAccessFile abspeichern, damit die Daten nach dem Neustart wieder da sind.

Ich habe mich da nicht sehr reingehängt, weil ich dachte, er findet schon was zu Serialisierung.

Dem Frager wurde gesagt, er müsse doch SQL machen und mit stored procedures arbeiten.

Weiter ging es dann, dass dem Frager doch wahrscheinlich die Qualifikation fehlt.

Außerdem müsse er doch einen Server mit X Kernen und Terrabyte-Festplatten aufbauen um darin seine (vielleicht 1 Kilobyte großen) Daten zu verwalten.
 
Zuletzt bearbeitet:

Barista

Top Contributor
oder ich mach es als Thread im Hintergrund
Threads sind schwergewichtig, eine Ressource im Betriebssystem, in der Anzahl auf ca. 200 begrenzt.

In die Collection kann man auch nicht alle Daten einfüllen, es könnten prinzipiell unendlich viele (ok in Wirklichkeit schon durch die Verwendung des Zahlentyps 32 bit in oder 64 bit long begrenzt, aber immer noch viel zu viel).
 

Barista

Top Contributor
Wenn man parallele Abarbeitungen haben möchte, dann machen Threads Sinn.
Bei der Verwendung von Threads muss man 2 Ziele unterscheiden.

1. Ziel

Die ursprünglich in Java eingebauten Green-Threads dienten der Concurrency, also asynchrone, nicht blockierende Schreibweise.

Dahinter standen Ideen wie Simula, im Computer lebt was, viel Organismen machen gleichzeitig was.

Außerdem erfordern grafische Oberflächen eine asynchrone, nicht blockierende Programmierung.

Es gibt Events, wie Mausklick, auf die reagiert wird. Nach der Reaktion muss der aktuelle Programmablauf verlassen werden.

2.Ziel

Nutzung mehr Rechenleistung durch Multicore.

Dahin wurden die Java-Threads entwickelt.

------

Mit virtuellen Threads wird Ziel 1 adressiert.

Irgendwelche Beispiele, wo Millionen virtuelle Threads erzeugt werden, finde ich da nicht hilfreich, um das Thema zu verstehen.

Die Abgrenzung zum Irrweg der reaktiven Programmierung ist da zum Verständnis sicher besser

------

Die Sache mit dem yield ist dabei nur ein Abfallprodukt, hier wird die Diskussion leider religiös fundamentalistisch.

------

Ich habe die Diskussion nicht angefangen, weil ich meine Arbeit/Aufgabe nicht hinbekomme, sondern weil ich im Umfeld keinen habe, mit dem ich mich darüber unterhalten kann.

Ich gehe manchmal zur Java User Group, da findet man aber auch nicht ohne weiteres einen passenden Gesprächspartner.
 

KonradN

Super-Moderator
Mitarbeiter
Threads sind schwergewichtig, eine Ressource im Betriebssystem, in der Anzahl auf ca. 200 begrenzt.

In die Collection kann man auch nicht alle Daten einfüllen, es könnten prinzipiell unendlich viele (ok in Wirklichkeit schon durch die Verwendung des Zahlentyps 32 bit in oder 64 bit long begrenzt, aber immer noch viel zu viel).
Virtuelle Threads sind auch Threads. Also ist das Thema mit der Einführung der virtuellen Threads doch erledigt.

Und wieso sollte jemand unbegrenzt Daten in eine Collection einfügen wollen? Das hat nie jemand verlangt oder erwähnt. Und auf das Missverständnis wurde bereits reagiert und es gab da entsprechende Hinweise.

Die Sache mit dem yield ist dabei nur ein Abfallprodukt, hier wird die Diskussion leider religiös fundamentalistisch.

Nein, hier ist nichts religiös oder fundamentalistisch. Leute nehmen sich Zeit, auf Deine Themen zu antworten und Du bekommst die Antworten offensichtlich in den falschen Hals oder verstehst sie einfach nicht. Und statt dann einfach nachzufragen kommen dann Totschlag-Argumente.

Damit drehen wir uns im Kreis und an der Stelle kommen wir nicht weiter.
 

KonradN

Super-Moderator
Mitarbeiter
Aber versuchen wir einfach einmal, etwas zu abstrahieren. Was ist denn so die Kernproblematik?

Meiner Meinung nach ist das Separation of Concerns

Es gibt eine Thematik und die will man implementieren / umsetzen. Und da kommt man dann schnell von Hölzchen zu Stöckchen und im Nu rennt man in irgendwelche Probleme rein, die man nicht mehr so einfach lösen kann. Und da ist ein Thema tatsächlich das saubere Design und eine saubere Unterteilung der Aufgaben.

Hier hat man dann einen Generator. Der soll dann irgendwas im Hintergrund machen können ... Dann wird er im Rahmen von Client Requests auf Server-Seite erstellt und man hat dann plötzlich ein Problem, dass ja der Generator einen Thread hält und referenziert wird und ....

Und man hat ein komplexes System gebaut mit womöglich nur einer Klasse, in der alles drin ist und das von der Komplexität einfach zu groß geworden ist. Das ist wie ein Anfänger, der da eine main Methode hat mit irgend einer Thematik und nun muss diese erweitert werden und der Überblick fehlt, weil da halt schon zig Verschachtellungen sind.

Die Lösung ist eine saubere Unterteilung. Bei der großen main Methode wären es einfache Methoden. So einfach ist es leider hier nicht. Aber es ist dennoch ähnlich.

Schauen wir uns doch die Kernaufgaben an:
Generator - das ist einfach etwas, das aus einem Zustand Z1 etwas erzeugt (G) und dabei ggf. den Zustand ändert (Z2). Das ist dann erst einmal eine einfache Methode mit Z1 -> (Z2, G). Das kann aber auch ein Supplier<G> sein und die Instanz hat intern einen Zustand Z. Das ist erst einmal eine relativ einfaches Interface bzw. ein einfacher Aufbau. Natürlich können es hoch komplexe Dinge sein, die der Generator macht, aber das wären dann wieder Dinge, die man dann heraus ziehen würde.

Wenn das nun etwas ist, das über Threads abgebildet werden soll, dann ist das erst einmal keine Problematik des Generators mehr. Dann habe ich einen Thread System (welcher Art auch immer), dem ich dann Tasks gebe, die besagen: Besorg das nächste Element und mach etwas damit.
Das System, das so Tasks abarbeitet, ist dann auch erst einmal universell. Das benötigt keine Details vom Generator oder was dann mit den Ergebnissen getan wird.

Das kommt dann eine Ebene höher. Da habe ich dann etwas, das den Generator kennt, das das Tasksystem kennt und das da dann Logik enthalten kann wie: Wenn vom Generator G in einer Queue weniger als n Elemente gespeichert sind, dann beauftrage ich einen Task, das ein Element von Generator G anfordert und dies dann in der Queue speichert. (Als eine Idee. Die genauen Anforderungen sind hier natürlich wichtig. Da hier nichts vorliegt, ist das nur ein Beispiel).

Und da kann dann auch irgend ein System dazu kommen, das dann darauf zugreift. Von mir aus eine Client/Server Lösung. Sobald ein Client Request kommt, dann wird da in so einem System ein Generator erzeugt und ein Client kann per Requests sich Elemente geben lassen. Auf dem Level kann dann auch die Logik hinterlegt sein: Clients mit letztem Request werden gemerkt. Wenn nach x Minuten kein Request eingegangen ist, dann wird der Generator der Clients gelöscht. (oder was auch immer).

So kann man dann eine Problematik relativ sauber und übersichtlich aufteilen. Auch Anpassungen sind nicht wirklich komplex. Das Task-System ist ja schon eine eigene Komponente. Die kann ich relativ schnell austauschen.



Die zweite Problematik aus meiner Sicht ist: Kenne Dein System!

Es ist super, wenn man die grundlegenden Konzepte kennt. Die sind oft sehr ähnlich. Also egal, welche Sprache man verwendet: Threads in irgend einer Art und Weise mit Mutex, Semaphoren, Locks, ... das gibt es überall. Und es macht dann auch tatsächlich Sinn, auf der Ebene dann zu recherchieren: Das wäre dann eine Fragestellung wie: Ich würde dieses oder jenes Konzept anwenden wollen: Wie geht das in (hier) Java.

Aber kein Sinn macht es, Implementierungsdetails anderer Sprachen erzwingen zu wollen. Hier das yield. Die Fragestellung: "Wie schreibe ich in Java einen Iterator" ist da dann schon eher sinnvoll, aber doch keine Frage nach dem konkreten yield.

Und dann ist halt wirklich wichtig, dass man weiss, was das System einen bietet. Das sind dann also die Klassen des Java Frameworks. Aber dann auch die Frameworks, die man ggf. verwendet. Und auf der Ebene gibt es dann auch ähnliche Vorgehen. Nur da sollte man dann auch die Konzepte hinterfragen und nicht eine konkrete Implementierung. Eine spezifische Klasse vom EntityFramework in .Net muss man nicht in Java suchen. Aber man kann durchaus hinterfragen: Wie kann der Code First Ansatz vom EntityFramework in Java umgesetzt werden? Und dann findet man Hibernate und Co und kann dann schauen, wie einzelne Probleme innerhalb der Frameworks gelöst wurden.

Das einfach nur um einmal eine etwas abstraktere Sicht auf die Problematik zu geben.
 

Barista

Top Contributor
Schauen wir uns doch die Kernaufgaben an:
Generator - das ist einfach etwas, das aus einem Zustand Z1 etwas erzeugt (G) und dabei ggf. den Zustand ändert (Z2). Das ist dann erst einmal eine einfache Methode mit Z1 -> (Z2, G). Das kann aber auch ein Supplier<G> sein und die Instanz hat intern einen Zustand Z. Das ist erst einmal eine relativ einfaches Interface bzw. ein einfacher Aufbau. Natürlich können es hoch komplexe Dinge sein, die der Generator macht, aber das wären dann wieder Dinge, die man dann heraus ziehen würde.
Stimmt.

Aber das heraus ziehen muss letztlich auch das asynchrone Modell realisieren.

Ich sehe, Du quälst Dich sehr damit. In einer Muschel entsteht eine Perle durch einen Schmerz.
 

Barista

Top Contributor
Es gibt eine Thematik und die will man implementieren / umsetzen. Und da kommt man dann schnell von Hölzchen zu Stöckchen und im Nu rennt man in irgendwelche Probleme rein, die man nicht mehr so einfach lösen kann. Und da ist ein Thema tatsächlich das saubere Design und eine saubere Unterteilung der Aufgaben.
Mir ging es nur darum, ob man so einen Generator bauen kann, ich habe kein konkretes Problem.
 

Barista

Top Contributor
Erst einmal der Thread Ansatz: Es ist unsinnig, einen Aufbau zu haben wie:
  • Ein Thread beantragt etwas von einem Generator. Das blockiert dann diesen Thread.
  • Der Generator generiert etwas auf dem eigenen Thread, gibt das Ergebnis zurück und blockiert dann den eigenen Thread
  • Der ursprüngliche Thread bekommt die Antwort und entsperrt damit wieder

Das macht doch nicht viel Sinn, oder?
Dies ist eine Ersatzlösung für Continuations/yield, die man im Netz finden kann.
Sie hat natürlich den Nachteil des hohen Ressourcenaufwandes.
Deshalb könnten eventuell die leichtgewichtigen virtuellen Threads eine Lösung sein.
 

KonradN

Super-Moderator
Mitarbeiter
Bitte beschäftige Dich mit Threads in Java ehe Du Aussagen triffst. Du hast im anderen Thread Code gezeigt, der einen virtuellen Thread erzeugt: Hast Du mal geschaut, was da zurück gegeben wird?

Hast Du mal die JavaDoc von java.lang.Thread angesehen? Dann wird auch schnell klar, dass Thread eben sowohl die "Platform threads" abdeckt als auch die "Virtual threads".

Eine Instanz von Thread kann also sowohl ein "platform thread" oder ein "virtual thread" sein.

"Im Hintergrund machen" ist nicht das Ziel, es geht um das asynchrone Programmiermodell.
Was bedeutet asynchrones Programmiermodell? Das besagt doch nichts anderes, als dass nicht auf die Beendigung einer Aufgabe gewartet wird sondern dass eben eine Aufgabe im Hintergrund erledigt wird und man in der Zwischenzeit andere Dinge machen kann.
 

Barista

Top Contributor
Was bedeutet asynchrones Programmiermodell? Das besagt doch nichts anderes, als dass nicht auf die Beendigung einer Aufgabe gewartet wird sondern dass eben eine Aufgabe im Hintergrund erledigt wird und man in der Zwischenzeit andere Dinge machen kann.

Es gibt sicher eine Definition für 'asynchrones Programmiermodell', findet man sicher im Netz.

Es erkläre es in eigenen Worten
Zum Beispiel in Android habe ich mit BLE (Blootooth Low Energy) gearbeitet.

Soweit ich mich erinnere, implementiert man in der Hauptklasse ein Interface.

Wenn man von einem BLE-Gerät das sogenannte Profil (Beschreibung, welche Dienste angeboten werden) haben will, gibt es dafür einen API-Aufruf.

Dann beendet sich der aktuelle Code.

Man wird wieder aufgerufen, wenn die Information da ist (callback).

Für die Unterstrukturen (Services, Charateristiken, Indications) gibt es jeweils weitere Aufrufe und Callbacks.

Man macht nichts im Hintergrund, das macht Android.

Für den Generator (yield) gab es im anderen Thread bereits Codebeispiel von anderen Leuts.

Dabei wird von der Sprache/System (Compiler VM oder was weiss ich) ein Code erzeugt, der den aktuellen Code so unterteilt, dass bei erneutem Abfragen nach dem yield weiter gearbeitet wird. Man spricht dort von Zustandsautomaten. Auch wird der jeweilige Stack-Abschnitt weggespeichert und beim Aufruf wieder hergestellt.
 


Schreibe deine Antwort... und nutze den </> Button, wenn du Code posten möchtest...
Ähnliche Java Themen
  Titel Forum Antworten Datum
Buroto Arrays generator Allgemeine Java-Themen 10
E Random Generator Allgemeine Java-Themen 6
L Generator für einen Parser implementieren Allgemeine Java-Themen 13
1 Name Generator für Videos Allgemeine Java-Themen 1
S QR-Code generator Allgemeine Java-Themen 2
A Audio Rechteck Generator Allgemeine Java-Themen 5
D Parser-generator für mathematische Funktionen Allgemeine Java-Themen 12
borobudur MVC Model Generator Allgemeine Java-Themen 2
boxi Registartions CodeBild generator Allgemeine Java-Themen 2
G Scanner-Generator zur Erkennung von Java Tokens Allgemeine Java-Themen 7
B Von neumann generator Allgemeine Java-Themen 7
F rxtx library mit virtuellem Comport Allgemeine Java-Themen 2
B Problem mit Virtuellem COM Port Allgemeine Java-Themen 1
B Ressourcenleck durch (virtuellen/echten) Thread Allgemeine Java-Themen 11
R 11 GB File lesen ohne zu extrahieren Filedaten Bereich für Bereich adressieren dann mit Multi-Thread id die DB importieren Allgemeine Java-Themen 3
urmelausdemeis Exception in thread "main" java.lang.Error: Unresolved compilation problem: Allgemeine Java-Themen 7
smarterToby Wie stoppe ich diesen Thread Allgemeine Java-Themen 4
A Thread.sleep Problem Allgemeine Java-Themen 2
J Thread started nur einmal Allgemeine Java-Themen 19
W Server-Thread schreibt nicht alle Dateien Allgemeine Java-Themen 6
OnDemand Logfile pro User / Thread Allgemeine Java-Themen 7
OnDemand Thread / Service abbrechen Allgemeine Java-Themen 3
Thallius Ist meine static Helper Class Thread save? Allgemeine Java-Themen 9
P Swing Exception in thread "AWT-EventQueue-0" java.lang.IndexOutOfBoundsException: npoints > xpoints.length || npoints > ypoints.length Allgemeine Java-Themen 5
B Thread.sleep() in EJB Container wie lösen? Allgemeine Java-Themen 11
S Ist das Neuzuweisen von Feldern atomic und damit Thread-Safe? Allgemeine Java-Themen 2
S Exception in thread "main" java.lang.NullPointerException at FamilienApp.main(FamilienApp.java:15) Allgemeine Java-Themen 1
J Einen Thread in einer Schleife Allgemeine Java-Themen 2
E HILFE !! Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/io/FileUtils Allgemeine Java-Themen 4
Flynn Thread-Problem... Allgemeine Java-Themen 2
G Thread-Programmierung Allgemeine Java-Themen 5
S Datei wird nicht gefunden Thread.currentThread().getContextClassLoader().getResourceAsStream() Allgemeine Java-Themen 1
G Beendet sich der Thread selbst?! Allgemeine Java-Themen 3
mrbig2017 Sleep wird ignoriert und der Thread wartet nicht Allgemeine Java-Themen 1
S Thread beenden Allgemeine Java-Themen 9
M Array aus Thread Objekten erstellen Allgemeine Java-Themen 2
Aruetiise Swing JOptionPane ohne denn Thread zu pausieren Allgemeine Java-Themen 1
M Nanosekunden-Pause innerhalb einen Thread-Loops Allgemeine Java-Themen 3
E Thread Exception Allgemeine Java-Themen 6
javaerd Binomialkoeffizient ausrechnen, Exception in thread "main" java.lang.StackOverflowError Allgemeine Java-Themen 6
T Merkwürdiges Thread-Verhalten Allgemeine Java-Themen 6
K Thread Problem Allgemeine Java-Themen 6
W Thread sleep 30 sekunden - wenn keine Antwort bis dahin neu senden Allgemeine Java-Themen 2
H Thread bleibt stehen bei jar in jar Allgemeine Java-Themen 1
J Threads HTTP Request (Thread) dauert lange - in Android Allgemeine Java-Themen 3
F CPU Last eines Thread ausfindig machen Allgemeine Java-Themen 0
V Compiler-Fehler Exception in thread "AWT-EventQueue-0" java.lang.IndexOutOfBoundsException: Index: 125, Size: 125 Allgemeine Java-Themen 11
Tausendsassa Threads Einen Thread sich selbst schließen lassen Allgemeine Java-Themen 17
P Threads BufferedImage, Thread Concurrency Allgemeine Java-Themen 1
M Klasse in separaten Thread ausführen.Wie genau? Allgemeine Java-Themen 2
llabusch Thread blockiert Dialog Allgemeine Java-Themen 1
J Thread wait() Allgemeine Java-Themen 2
V Thread.sleep und InterruptedException? Allgemeine Java-Themen 1
G Thread nicht von GC zerstört Allgemeine Java-Themen 6
J Wie erschaffe ich einen sicheren Datenaustausch zwischen Thread und Nicht-Threads Allgemeine Java-Themen 8
Sogomn Thread blocken bis Taste gedrückt Allgemeine Java-Themen 5
T Starten vom Thread Allgemeine Java-Themen 3
T Wait/Notify() bei Thread Allgemeine Java-Themen 6
J Exception in thread "main" java.lang.NoClassDefFoundError Allgemeine Java-Themen 4
M Exception in thread "AWT-EventQueue-0" Allgemeine Java-Themen 6
Q Thread wacht nicht auf Allgemeine Java-Themen 7
T Fragen zum Thread-Thema Allgemeine Java-Themen 4
T Threads Input/Output im Thread - Datei ohne Inhalt Allgemeine Java-Themen 1
T Fragen zum Thread-Thema Allgemeine Java-Themen 9
C Threads Variablen in einem Thread Aktualisieren Allgemeine Java-Themen 17
U Thread beenden Allgemeine Java-Themen 3
W Threads Mit Thread und Runtime externe Programme öffnen Allgemeine Java-Themen 0
N Thread interrupt Status debuggen Allgemeine Java-Themen 6
A Thread: Code paralell ausführen in mehreren Instanzen Allgemeine Java-Themen 1
E Threads linkedlist/multi-thread problem Allgemeine Java-Themen 3
B Erkennen, wann Prozess beendet ist, dann Thread beenden. Allgemeine Java-Themen 6
A Thread Fehler absichtlich provozieren Allgemeine Java-Themen 3
B Threads Java Thread kommunizieren Allgemeine Java-Themen 12
N Thread Sicherheit im komplexen Datenmodell Allgemeine Java-Themen 7
K Thread richtig benutzen Allgemeine Java-Themen 3
K Exception in thread "AWT-EventQueue-1" Allgemeine Java-Themen 2
vandread Problem bei kleiner Thread-Übung Allgemeine Java-Themen 2
G Thread erzeugt nicht plausible NullPointerException Allgemeine Java-Themen 7
H Netbeans Warning bei Thread.sleep in Schleife Allgemeine Java-Themen 4
P [Thread] Scheint nicht Sequenziell zu Arbeiten Allgemeine Java-Themen 9
A eine test thread.join() frage Allgemeine Java-Themen 2
tuttle64 Verständnisprobleme mit Thread Locks Allgemeine Java-Themen 4
G Threads Thread bei Datenabfrage Allgemeine Java-Themen 3
S Thread anhalten per Button ? Allgemeine Java-Themen 3
E Thread Programmierung Allgemeine Java-Themen 2
S Threads ServerSocket-Thread soll schlafen, bis er gebraucht wird Allgemeine Java-Themen 2
V Thread schneller stoppen Allgemeine Java-Themen 2
V anstatt thread.join() einfach while schleife? Allgemeine Java-Themen 8
B Mausbewegung im Thread erkennen (hoch/runter) Allgemeine Java-Themen 6
G Linux/C++/Pthreads auf JVM zugreifen, thread safe? Allgemeine Java-Themen 10
K Threads Probleme mit Thread Allgemeine Java-Themen 13
K Threads Thread überprüfen Allgemeine Java-Themen 3
Z Threads Thread für einen Client Allgemeine Java-Themen 9
M Thread JavaFish Allgemeine Java-Themen 10
G Thread.sleep Allgemeine Java-Themen 12
M Threads Viele Aufrufe aus Thread, komisches Verhalten Allgemeine Java-Themen 8
B Threads Main Thread warten auf abgebrochen Task warten lassen Allgemeine Java-Themen 25
K Timer Thread Allgemeine Java-Themen 8
M Methoden Static Methoden und Thread??? Allgemeine Java-Themen 4
N java.lang.IllegalMonitorStateException: object not locked by thread before notify() Allgemeine Java-Themen 2

Ähnliche Java Themen

Neue Themen


Oben