it-swarm.com.de

Wie lasse ich einen Java Thread auf die Ausgabe eines anderen Threads warten?

Ich erstelle eine Java) -Anwendung mit einem Anwendungslogikthread und einem Datenbankzugriffsthread. Beide bestehen für die gesamte Lebensdauer der Anwendung und müssen auf dem ausgeführt werden Gleichzeitig (man spricht mit dem Server, man spricht mit dem Benutzer; wenn die App vollständig gestartet ist, brauche ich beide von ihnen, um zu arbeiten).

Beim Start muss ich jedoch sicherstellen, dass der App-Thread zunächst wartet, bis der DB-Thread bereit ist (derzeit ermittelt durch Abfragen einer benutzerdefinierten Methode dbthread.isReady()). Ich hätte nichts dagegen, wenn App-Thread blockiert, bis der DB-Thread fertig war.

Thread.join() sieht nicht nach einer Lösung aus - der Datenbank-Thread wird nur beim Herunterfahren der App beendet.

while (!dbthread.isReady()) {} funktioniert, aber die leere Schleife verbraucht eine Menge Prozessorzyklen.

Irgendwelche anderen Ideen? Vielen Dank.

123
Piskvor

Ich würde wirklich empfehlen, dass Sie ein Tutorial wie Suns Java Concurrency durcharbeiten, bevor Sie in die magische Welt des Multithreading einsteigen.

Es gibt auch eine Reihe guter Bücher (Google für "Concurrent Programming in Java", "Java Concurrency in Practice").

Um zu Ihrer Antwort zu gelangen:

In Ihrem Code, der auf dbThread warten muss, müssen Sie ungefähr Folgendes haben:

//do some work
synchronized(objectYouNeedToLockOn){
    while (!dbThread.isReady()){
        objectYouNeedToLockOn.wait();
    }
}
//continue with work after dbThread is ready

In der Methode Ihres dbThread müssten Sie ungefähr so ​​vorgehen:

//do db work
synchronized(objectYouNeedToLockOn){
    //set ready flag to true (so isReady returns true)
    ready = true;
    objectYouNeedToLockOn.notifyAll();
}
//end thread run method here

Das objectYouNeedToLockOn, das ich in diesen Beispielen verwende, ist vorzugsweise das Objekt, das Sie gleichzeitig von jedem Thread bearbeiten müssen, oder Sie können zu diesem Zweck ein separates Object erstellen (ich würde nicht empfehlen, das zu erstellen Methoden selbst synchronisiert):

private final Object lock = new Object();
//now use lock in your synchronized blocks

Zum besseren Verständnis:
Es gibt andere (manchmal bessere) Wege, um das Obige zu tun, z. mit CountdownLatches, etc. Seit Java 5) gibt es im Java.util.concurrent - Paket und den Unterpaketen eine Menge raffinierter Parallelitätsklassen. Sie müssen wirklich Material finden online, um Parallelität kennenzulernen oder ein gutes Buch zu bekommen.

126
Herman Lintvelt

Verwenden Sie ein CountDownLatch mit einem Zähler von 1.

CountDownLatch latch = new CountDownLatch(1);

Jetzt im App-Thread

latch.await();

Wenn Sie fertig sind, führen Sie im Datenbank-Thread Folgendes aus:

latch.countDown();
133
pdeva

Anforderung ::

  1. Warten auf die Ausführung des nächsten Threads, bis der vorherige beendet ist.
  2. Der nächste Thread darf nicht gestartet werden, bis der vorherige Thread gestoppt ist, unabhängig vom Zeitaufwand.
  3. Es muss einfach und leicht zu bedienen sein.

Antworten ::

@See Java.util.concurrent.Future.get () doc.

future.get () Wartet bei Bedarf, bis die Berechnung abgeschlossen ist, und ruft dann das Ergebnis ab.

Job erledigt!! Siehe folgendes Beispiel

import Java.util.concurrent.Callable;
import Java.util.concurrent.ExecutionException;
import Java.util.concurrent.ExecutorService;
import Java.util.concurrent.Executors;

import org.junit.Test;

public class ThreadTest {

    public void print(String m) {
        System.out.println(m);
    }

    public class One implements Callable<Integer> {

        public Integer call() throws Exception {
            print("One...");
            Thread.sleep(6000);
            print("One!!");
            return 100;
        }
    }

    public class Two implements Callable<String> {

        public String call() throws Exception {
            print("Two...");
            Thread.sleep(1000);
            print("Two!!");
            return "Done";
        }
    }

    public class Three implements Callable<Boolean> {

        public Boolean call() throws Exception {
            print("Three...");
            Thread.sleep(2000);
            print("Three!!");
            return true;
        }
    }

    /**
     * @See Java.util.concurrent.Future.get() doc
     *      <p>
     *      Waits if necessary for the computation to complete, and then
     *      retrieves its result.
     */
    @Test
    public void poolRun() throws InterruptedException, ExecutionException {
        int n = 3;
        // Build a fixed number of thread pool
        ExecutorService pool = Executors.newFixedThreadPool(n);
        // Wait until One finishes it's task.
        pool.submit(new One()).get();
        // Wait until Two finishes it's task.
        pool.submit(new Two()).get();
        // Wait until Three finishes it's task.
        pool.submit(new Three()).get();
        pool.shutdown();
    }
}

Ausgabe dieses Programms:

One...
One!!
Two...
Two!!
Three...
Three!!

Sie können sehen, dass es 6 Sekunden dauert, bis die Aufgabe abgeschlossen ist, die größer ist als die anderer Threads. Future.get () wartet also, bis die Aufgabe erledigt ist.

Wenn Sie future.get () nicht verwenden, wartet es nicht, bis es fertig ist, und führt basierend auf dem Zeitverbrauch aus.

Viel Glück mit Java Parallelität.

22
Ash
public class ThreadEvent {

    private final Object lock = new Object();

    public void signal() {
        synchronized (lock) {
            lock.notify();
        }
    }

    public void await() throws InterruptedException {
        synchronized (lock) {
            lock.wait();
        }
    }
}

Verwenden Sie diese Klasse dann so:

Erstelle ein ThreadEvent:

ThreadEvent resultsReady = new ThreadEvent();

In der Methode wartet dies auf Ergebnisse:

resultsReady.await();

Und in der Methode, die die Ergebnisse erstellt, nachdem alle Ergebnisse erstellt wurden:

resultsReady.signal();

EDIT:

(Sorry, dass du diesen Beitrag bearbeitet hast, aber dieser Code hat einen sehr schlechten Rennzustand und ich habe nicht genug Ruf, um einen Kommentar abzugeben.)

Sie können dies nur verwenden, wenn Sie zu 100% sicher sind, dass signal () nach await () aufgerufen wird. Dies ist der Hauptgrund, warum Sie kein Java Objekt wie beispielsweise Windows Events verwenden können.

Das, wenn der Code in dieser Reihenfolge ausgeführt wird:

Thread 1: resultsReady.signal();
Thread 2: resultsReady.await();

dann Thread 2 wird für immer warten. Dies liegt daran, dass Object.notify () nur einen der aktuell ausgeführten Threads aktiviert. Ein später wartender Thread wird nicht geweckt. Dies unterscheidet sich stark von der erwarteten Funktionsweise von Ereignissen, bei denen ein Ereignis signalisiert wird, bis a) gewartet oder b) explizit zurückgesetzt wird.

Hinweis: Meistens sollten Sie notifyAll () verwenden, dies ist jedoch für das oben beschriebene Problem "Für immer warten" nicht relevant.

8
Lourens Coetzer

Versuchen Sie CountDownLatch Klasse aus dem Java.util.concurrent -Paket, das Synchronisationsmechanismen auf höherer Ebene bietet, die weitaus weniger fehleranfällig sind als die auf niedriger Ebene.

7
WMR

Viele richtige Antworten, aber ohne ein einfaches Beispiel. Hier ist eine einfache und einfache Methode, um CountDownLatch zu verwenden:

//inside your currentThread.. lets call it Thread_Main
//1
final CountDownLatch latch = new CountDownLatch(1);

//2
// launch thread#2
new Thread(new Runnable() {
    @Override
    public void run() {
        //4
        //do your logic here in thread#2

        //then release the lock
        //5
        latch.countDown();
    }
}).start();

try {
    //3 this method will block the thread of latch untill its released later from thread#2
    latch.await();
} catch (InterruptedException e) {
    e.printStackTrace();
}

//6
// You reach here after  latch.countDown() is called from thread#2
6
Maher Abuthraa

Sie können dies mit einem Exchanger -Objekt tun, das von den beiden Threads gemeinsam genutzt wird:

private Exchanger<String> myDataExchanger = new Exchanger<String>();

// Wait for thread's output
String data;
try {
  data = myDataExchanger.exchange("");
} catch (InterruptedException e1) {
  // Handle Exceptions
}

Und im zweiten Thread:

try {
    myDataExchanger.exchange(data)
} catch (InterruptedException e) {

}

Nehmen Sie, wie andere bereits gesagt haben, nicht diesen unbeschwerten Code, sondern kopieren Sie ihn einfach und fügen Sie ihn ein. Lesen Sie zuerst etwas.

6
kgiannakakis

Die Future Schnittstelle aus dem Java.lang.concurrent package soll den Zugriff auf Ergebnisse ermöglichen, die in einem anderen Thread berechnet wurden.

Schauen Sie sich FutureTask und ExecutorService an, um eine vorgefertigte Vorgehensweise zu finden.

Ich würde das Lesen von Java Concurrency In Practice jedem empfehlen, der sich für Concurrency und Multithreading interessiert. Es konzentriert sich offensichtlich auf Java, aber es gibt jede Menge Fleisch für alle, die auch in anderen Sprachen arbeiten.

4
Bill Michell

Dies gilt für alle Sprachen:

Sie möchten ein Event-/Listener-Modell haben. Sie erstellen einen Listener, um auf ein bestimmtes Ereignis zu warten. Das Ereignis wird in Ihrem Arbeitsthread erstellt (oder signalisiert). Dadurch wird der Thread blockiert, bis das Signal empfangen wird, anstatt ständig abzufragen, ob eine Bedingung erfüllt ist, wie z. B. die aktuelle Lösung.

Ihre Situation ist eine der häufigsten Ursachen für Deadlocks. Stellen Sie sicher, dass Sie den anderen Thread unabhängig von eventuell aufgetretenen Fehlern signalisieren. Beispiel - wenn Ihre Anwendung eine Ausnahme auslöst - und niemals die Methode aufruft, um dem anderen zu signalisieren, dass die Dinge abgeschlossen sind. Dadurch wird es so, dass der andere Thread nie "aufwacht".

Ich schlage vor, dass Sie sich mit den Konzepten der Verwendung von Ereignissen und Ereignishandlern befassen, um dieses Paradigma besser zu verstehen, bevor Sie Ihren Fall implementieren.

Alternativ können Sie einen blockierenden Funktionsaufruf mit einem Mutex verwenden. Dadurch wartet der Thread, bis die Ressource frei ist. Dazu benötigen Sie eine gute Thread-Synchronisation, wie zum Beispiel:

Thread-A Locks lock-a
Run thread-B
Thread-B waits for lock-a
Thread-A unlocks lock-a (causing Thread-B to continue)
Thread-A waits for lock-b 
Thread-B completes and unlocks lock-b
2
Klathzazt

Wenn Sie etwas schnelles und schmutziges wollen, können Sie einfach einen Thread.sleep () - Aufruf in Ihre while-Schleife einfügen. Wenn Sie die Datenbankbibliothek nicht ändern können, gibt es wirklich keine andere einfache Lösung. Das Abrufen der Datenbank, bis eine Wartezeit vorliegt, führt nicht zu einer Beeinträchtigung der Leistung.

while (!dbthread.isReady()) {
  Thread.sleep(250);
}

Kaum etwas, das man eleganten Code nennen könnte, aber die Arbeit erledigt.

Wenn Sie den Datenbankcode ändern können, ist es besser, einen Mutex zu verwenden, wie in anderen Antworten vorgeschlagen.

2
Mario Ortegón

Sie können in einem Thread aus einer blockierenden Warteschlange lesen und in einem anderen Thread darauf schreiben.

2
Ingo

Schon seit

  1. join() wurde ausgeschlossen
  2. sie haben bereits CountDownLatch und verwendet
  3. Future.get () wird bereits von anderen Experten vorgeschlagen,

Sie können auch andere Alternativen in Betracht ziehen:

  1. invokeAll from ExecutorService

    invokeAll(Collection<? extends Callable<T>> tasks)
    

    Führt die angegebenen Aufgaben aus und gibt eine Liste der Futures zurück, die ihren Status und ihre Ergebnisse enthalten, wenn alle abgeschlossen sind.

  2. ForkJoinPool oder newWorkStealingPool von Executors (seit Java 8 Release)

    Erstellt einen Thread-Pool, der Arbeit stiehlt, wobei alle verfügbaren Prozessoren als Zielparallelitätsebene verwendet werden.

1
Ravindra babu