Multithreading: Service(Task), wait und continue

Kababär

Top Contributor
Hi,

ich probiere mich gerade etwas an Multithreading, aber so ganz scheine ich es noch nicht verstanden zu haben.
Mein Ziel ist es, einen Task, der eine (oder mehrere) Aufgaben erfüllt, zu pausieren und bei Bedarf wieder die Arbeit fortzuführen.
Das heißt er zählt einen long-Wert hoch (mein gewünschtes Ergebnis):

1,2,3,4,5,6,7,8 pausiert, wartet 50ms, arbeitet weiter: 9,10,11,12,13,14,15,16, pausiert...

Meine Idee war es, wait() und notify() zu benutzen. Doch das Aufwecken funktioniert nicht.

Zum Testen verwende ich ein minimalistisches Beispiel, bei dem eine Zähler hochgezählt wird und bei mod 8 gestoppt wird. Nach 50ms soll die Arbeit wieder aufgenommen werden.
Problem: Der Task wird nicht aufgweckt.
Mein bisheriger Code:

Code:
public class ConcurrenTest {

    public static void main(String[] args) {
        MyService runner = new MyService();
        runner.start();
        try {
            Thread.sleep(50);
            runner.resume();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

class MyService extends Service<Void> {

    private boolean paused;

    @Override
    protected Task<Void> createTask() {
        int limit = 20;
        int current = 0;
        while (current < limit) {
            if (paused) waitWhilePaused();

            System.out.println(current);

            if ((current > 0) && (current % 8) == 0) {
                pause();
            }
            current++;
        }
        return null;
    }

    synchronized void pause() {
        paused = true;
    }


    synchronized void waitWhilePaused() {
        while (paused) {
            try {
                System.out.println("Ich warte ...");
                wait();
                System.out.println("Ich warte jetzt nicht mehr");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public synchronized void resume() {
        System.out.println("Resume");
        paused = false;
        notifyAll();
    }

}

Meine erste Überlegung war, dass ein Thread/Service sich nicht selbst wecken kann, also habe ich runner.resume() durch new MyServce().resume() ersetzt, aber ohne Erfolg.

Eigentlich habe ich erwartet, dass der Task im Hintergrund weiterläuft (obwohl er pausiert wurde), aber den Output für den User erst erkennbar macht, wenn er wieder weiterläuft.
Also:
1,2,3,4,5,6,7,8 pausiert, wartet 50ms, arbeitet weiter: 9,10,11,12,13,14,15,16, 17,18, 19, 20, 21, 22, 23, 24 .... (er hat weiter gearbeitet) pausiert...

Code:
Mein momentanes Ergebnis ist:
0
1
2
3
4
5
6
7
8
Ich warte ...

Und da hört es auf. Ich bin mir sicher, wenn ich ihn wecken könnte, würde mein erwartetes Ergebnis rauskommen (das ich dann noch lösen müsste um mein gewünschtes Ergebnis zu bekommen).

Kann mir jemand helfen? Wisst ihr was ich meine? Falls nicht, erkläre ich es gerne nochmal.
 

Kababär

Top Contributor
However, once the Service has been initialized and started, it may only thereafter be used from the FX thread.

Meinst du das? :D Mein Fehler.
Ich wollte ein seltsames Verhalten meiner Applikation reproduzieren, da ich den Code hier nicht reinstellen kann (wegen Lizenzgründen). Dann muss ich wohl so lange rumfriemeln und lesen bis es klappt.
Danke für den Hinweis
 

mrBrown

Super-Moderator
Mitarbeiter
Man könnte auch von der Methodendeklaration drauf kommen ;)

createTask soll den Task erstellen und zurückgeben - es ist nicht selbst der parallel auszuführende Code
 

Kababär

Top Contributor
Ahhhh... du meinst also so

Code:
@Override
protected Task<Void> createTask() {
    return new Task<Void>(){

        @Override
        protected Void call() throws Exception {
            int limit = 20;
            int current = 0;
            while (current < limit) {
                if (paused) waitWhilePaused();

                System.out.println(current);

                if ((current > 0) && (current % 8) == 0) {
                    pause();
                }
                current++;
            }
            return null;
        }
    };

}
 

mrBrown

Super-Moderator
Mitarbeiter
Das sieht schon richtiger aus ;)

paused sollte noch volatile sein, sonst läufst du gleich ins nächste Problem
 

Kababär

Top Contributor
Dass die Variable im "Cache" (also eigentlich RAM) gespeichert und nicht jedes Mal von der Festplatte gelesen wird und ich somit mit einem falschen (nicht aktuellen) Wert weiterrechnen würde? :D
 

mrBrown

Super-Moderator
Mitarbeiter
Dass die Variable im "Cache" (also eigentlich RAM) gespeichert und nicht jedes Mal von der Festplatte gelesen wird und ich somit mit einem falschen (nicht aktuellen) Wert weiterrechnen würde? :D
Nein, dass die Schleife ihre eigene lokale Kopie der Variable hat, die nicht zwingend geändert wird, wenn die Instanzvariable geändert wird ;)
 

Blender3D

Top Contributor
Falls mehrere Threads Zugriff auf dieselbe Variable haben, wird die JVM vermutlich jedem Thread eine eigene Kopie der Variablen zugestehen. Änderungen an der Variablen durch einen Thread werden von anderen Threads bemerkt, womöglich aber auch nicht. Hier kommt der Vorteil von volatile gegenüber synchronised zum Tragen.

Mithilfe des Schlüsselworts volatile lässt sich der Zugriff auf eine Variable synchronisieren. Ist eine Variable als volatile deklariert, muss die JVM sicherstellen, dass alle zugreifenden Threads ihre Kopien aktualisieren, sobald die Variable geändert wird.

Ruft ein Thread synchronisierte Methoden oder Codeblocks auf, muss er den Lock des Objekts übernehmen. Verlässt der Thread den synchronisierten Code, muss der Lock wieder freigegeben werden. Das Übernehmen und Freigeben von Objekt-Locks erfordert Zeit und Ressourcen. Mithilfe des Schlüsselworts volatile lässt sich dieser Aufwand vermeiden.

Nachteile

Allerdings hat auch die Verwendung von volatile seinen Preis. Den Wert eines volatile-Feldes zwischen allen Threads synchronisiert zu halten, beansprucht ebenfalls Ressourcen. Am besten drückt es wahrscheinlich die Java-Spezifikation selbst aus: „Ein Feld kann als volatile deklariert werden. In diesem Fall muss ein Thread seine Arbeitskopie des Feldes jedes Mal, wenn er auf die Variable zugreift, mit der Masterkopie abgleichen.“

Wann das Schlüsselwort volatile zum Einsatz kommt und wann besser die synchronized-Methode, hängt von der jeweiligen Anwendung ab. Wer das nächste Mal einen Datenzugriff synchronisieren muss, sollte beide Optionen in Erwägung ziehen. Erst dann zeigt sich, welche von beiden die Aufgabe am besten erfüllt.
 

Kababär

Top Contributor
Ok, verstanden. Aber ich habe nicht verstanden wieso mehrere Threads bei mir auf die gleiche Variable zugreifen. Ich erstelle doch lediglich ein Task (createTask<Void>). Oder erstellt Service unter der Haube tatsächlich ein Thread-Pool?
 

mrBrown

Super-Moderator
Mitarbeiter
Ok, verstanden. Aber ich habe nicht verstanden wieso mehrere Threads bei mir auf die gleiche Variable zugreifen. Ich erstelle doch lediglich ein Task (createTask<Void>). Oder erstellt Service unter der Haube tatsächlich ein Thread-Pool?
Ja - der Sinn des ganzen ist doch, dass die Berechnung in einem anderem Thread läuft, als dein Aufruf von resume() ;)
 

Kababär

Top Contributor
Ok das heißt, dass die Berechnung fortgeführt wird, auch wenn ich pause() aufrufe? (so ist auch jedenfalls meine Beobachtung nach den eingebauten Änderungen)
Wie pausiere ich ihn wirklich, ohne dass er weiterrechnet? Denn mein Wunsch ist es, das Ergebnis der Berechnung in Realzeit an die GUI zu schicken.

Vorerst habe ich den Service außenvor gelassen und starte meinen Task nun mit
Code:
new Thread(task).start()
Jedoch hat auch hier pause() nur Auswirkungen auf die GUI, nicht auf die eigentliche Arbeit des Tasks, denn dieser rechnet weiter und sendet dann alle Ergebnisse an die GUI weiter, die er in der Zeit berechnet hat, in der die GUI pausiert war.
 

Kababär

Top Contributor
Also meine Task-Klasse sieht so aus:

Code:
/**
* Created by Dave on 17.08.2017.
*/
public class ConcurrencyTest {

    public static void main(String[] args) {
        MyTask myTask = new MyTask();
        myTask.playerStatusProperty().addListener((observable, oldValue, newValue) -> {
            switch (newValue) {
                case HALTED:
                    System.out.println("Halted");
                    break;
                case READY:
                    System.out.println("Ready");
                    break;
                case RUNNING:
                    System.out.println("Running");
                    break;
                case STOPPED:
                    System.out.println("Stopped");
                    break;
                case UNKNOWN:
                    System.out.println("Unknown");
                    break;
                default:
                    System.out.println("Default");
                    break;
            }
        });

        myTask.valueProperty().addListener((observable, oldValue, newValue) -> {
            System.out.println(newValue);
        });

        try {
            new Thread(myTask).start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

class MyTask extends Task<Integer> {

    private SimpleObjectProperty<PlayerStatus> playerStatus;
    private volatile boolean shouldHalt;
    private final Object lock = new Object();


    public MyTask() {
        this.playerStatus = new SimpleObjectProperty<>(PlayerStatus.READY);
        this.shouldHalt = false;
    }

    @Override
    protected Integer call() throws Exception {
        this.playerStatus.setValue(PlayerStatus.RUNNING);
        init();
        for (int i = 0; i < 100; i++) {
            if (shouldHalt) halt();
            updateValue(i);
        }
        return null;
    }

    private void init() {
        /**
         * Daten laden und so
         */
    }

    private void halt() {
        synchronized (lock) {
            try {
                playerStatus.setValue(PlayerStatus.HALTED);
                System.out.println("Halt Task, bevor wait.");
                lock.wait();
                System.out.println("Nach wait.");
            } catch (InterruptedException e) {
                playerStatus.setValue(PlayerStatus.UNKNOWN);
                System.out.println("Fehler: " + e.toString());
                e.printStackTrace();
            }
        }
    }


    public synchronized void pause() {
        System.out.println("Pausiere Task");
        shouldHalt = true;
        playerStatus.setValue(PlayerStatus.HALTED);
    }

    public synchronized void resume() {
        System.out.println("Resume Task, vor synchronized");
        synchronized (lock) {
            System.out.println("Resume Task, vor notify");
            shouldHalt = false;
            playerStatus.setValue(PlayerStatus.RUNNING);
            lock.notify();
            System.out.println("Resume Task, nach notify");
        }
    }

    public synchronized void stop() {
        this.cancel(true);
        playerStatus.setValue(PlayerStatus.STOPPED);
        while (!this.isCancelled()) {
            updateValue(null);
        }
    }

    public PlayerStatus getPlayerStatus() {
        return playerStatus.get();
    }

    public ObjectProperty<PlayerStatus> playerStatusProperty() {
        return playerStatus;
    }

    public void setPlayerStatus(PlayerStatus playerStatus) {
        this.playerStatus.set(playerStatus);
    }
}

enum PlayerStatus {
    READY, RUNNING, STOPPED, HALTED, UNKNOWN;
}

Die main ist nur zu Testzwecken da. Die Logik wird in meinem Controller folgendermaßen realisiert:
Code:
 playButton.setOnAction(e -> {
            if (pausableTask.getPlayerStatus() == PlayerStatus.HALTED) {
                pausableTask.resume();
            } else if (pausableTask.getPlayerStatus() == PlayerStatus.READY) {
                runNewTask();
            } else if (pausableTask.getPlayerStatus() == PlayerStatus.RUNNING) {
                pausableTask.pause();
            } else if (pausableTask.getPlayerStatus() == PlayerStatus.STOPPED) {
                pausableTask.cancel(true);
                while (pausableTask.isRunning() && !pausableTask.isCancelled()) ;
                pausableTask = new VideoDecodeTask(_FILENAME);
                runNewTask();
            } else if (pausableTask.getPlayerStatus() == PlayerStatus.UNKNOWN) {
                System.out.println("Was soll ich bloß tun...");
            }
        });

Ich höre auf die "valueProperty" des Tasks, da dieser ja eigentlich dann geupdated wird, sobald sich der Wert ändert.
Angenommen ich downloade etwas und pro Sekunde lade ich 1% der Datei runter. Wenn ich bei 50% den Task pausiere, dann wird die ProgressBar nicht aktualisiert (man denkt, es pausiert wirklich). Wird der Task dann nach bspw. 50 Sekunden resumed, dann steht der Fortschrittsbalken dann auf 100%. Und das ist das, was mich verwirrt..
 

JuKu

Top Contributor
Ehrlich gesagt sieht der Code nicht besonders toll aus und ich bin mir auch nicht ganz sicher, was du überhaupt brauchst.
Fakt ist: Es ist in Java nicht möglich, einen Thread wirklich (ohne Overhead) zu pausieren, pause() und resume() sind veraltet:
http://javawiki.sowas.com/doku.php?id=java:thread-pausieren
http://javawiki.sowas.com/doku.php?id=java:thread-pausieren
Einfacher wäre es, einen Executor Service dafür zu nehmen und die Variable volatile zu setzen.

Oder aber du setzt ein Flag:
Java:
class MyThread implements Runnable {

    protected volatile boolean paused = false;

    public MyThread () {
        //...
    }

    public void run () {
        while (true) {
            if (!paused) {
                //do something
            }
        }
    }

}

Aber das ist das ineffizienteste, was ich kenne!
So wie du dir das vorstellst, wird das nicht funktionieren, denn mit wait() oder notify() musst du auch von außerhalb des Threads den Thread steuern. Sinniger wäre es, ein Thread.sleep() in die while Schleife einzubauen, denn du willst ja nur eine gewisse Zeit warten.
 

Kababär

Top Contributor
Das habe ich doch schon alle JuKu. Mir geht es darum, den Fehler zu finden.Wieso wird die GUI angehalten aber die Berechnungen nicht? Wie kann das sein?!
 

mrBrown

Super-Moderator
Mitarbeiter
So wie du dir das vorstellst, wird das nicht funktionieren, denn mit wait() oder notify() musst du auch von außerhalb des Threads den Thread steuern.
Zumindest das, was er sich zu Anfang vorgestellt hat, funktioniert so, wie er sich das vorstellte^^


Das habe ich doch schon alle JuKu. Mir geht es darum, den Fehler zu finden.Wieso wird die GUI angehalten aber die Berechnungen nicht? Wie kann das sein?!
Wie sehen denn die souts aus?
 

Kababär

Top Contributor
Ich glaube ich habe den Fehler entdeckt. In der API, die ich benutze, wird intern ein Zähler verwendet. D.h. jedes mal von ich vom "Pause-Zustand" in den "Play-Zustand" zurückkehre, wird alles extrem schnell runtergerattert.
Mit einem extra delay, den ich selbst steuere und zähle, sieht es gut aus. Nun.. habe ihn als long deklariert. Es scheint zu funktionieren.

Hab mich so sehr aufs Multithreading fokussiert, dass ich das eigentliche Problem übersehen habe. Schande über mich.

Edit: Um das Problem zu lokalisieren, mehrere Tage gebraucht, um es zu lösen, 3 Minuten... Achja.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
W Multithreading Alphabet Allgemeine Java-Themen 3
T Multithreading: Wie viele Threads sollte ich erstellen? Allgemeine Java-Themen 12
J Threads Multithreading Allgemeine Java-Themen 15
K Multithreading plattform übergreifend? Allgemeine Java-Themen 3
R Bruteforce hashes mit multithreading. Funktioniert das so? Allgemeine Java-Themen 0
B Threads Multithreading Threads sollen warten Allgemeine Java-Themen 12
M JUnit & Multithreading - sehr seltener Fehler Allgemeine Java-Themen 3
C Ressourcensparendes Multithreading Allgemeine Java-Themen 3
A Multithreading mit JButtons Allgemeine Java-Themen 5
S Threads Multithreading- langsamer als Singlethreading-Programm Allgemeine Java-Themen 6
D Threads Multithreading Allgemeine Java-Themen 25
A MultiThreading.. Scheduling-Problem? Allgemeine Java-Themen 10
M Multithreading Problem Allgemeine Java-Themen 3
dayaftereh Multithreading Allgemeine Java-Themen 16
E Multithreading and volatile Allgemeine Java-Themen 10
J Wie die gleichzeitige Ausführung mehrerer Tasks trotz Multithreading verhindern? Allgemeine Java-Themen 2
G multithreading, concurrency conveyor belt beispiel Allgemeine Java-Themen 2
A Problem mit Zufallszahlen und Multithreading Allgemeine Java-Themen 14
I Problem mit Multithreading Allgemeine Java-Themen 4
H Singleton und MultiThreading [erledigt] Allgemeine Java-Themen 3
C Collection Multithreading? Allgemeine Java-Themen 33
O Multithreading mit Java 5 u den Concurrency APIs Allgemeine Java-Themen 7
O Multithreading und Multiprozessor Allgemeine Java-Themen 4
K Multithreading bei statischen Methoden Allgemeine Java-Themen 2
T ungewöhnliche Exception (Multithreading und JList) Allgemeine Java-Themen 10
K Frage zu ProgressBars, Algorithmen und Multithreading ->F Allgemeine Java-Themen 2
flashfactor Multithreading-Problem Allgemeine Java-Themen 4
izoards JAR als Windows Service mit Appache Procrun (prunsrv) Allgemeine Java-Themen 6
A Zweite Service Klasse beim Kompilieren Allgemeine Java-Themen 6
O Service oder Controller Allgemeine Java-Themen 6
OnDemand Thread / Service abbrechen Allgemeine Java-Themen 3
X Kapselung Wie würdet ihr ein Service Layer erreichbar machen ... Allgemeine Java-Themen 62
H Aufruf eines Web Service anhand übergebenen Parameter Allgemeine Java-Themen 2
X Threads Java Chached Executors Service Allgemeine Java-Themen 12
P Entity Objekt Methoden vs Service methoden Allgemeine Java-Themen 2
K Hilfe bei GUI für Pizza-Service Allgemeine Java-Themen 11
L TV Programm API/Web Service o.ä. Allgemeine Java-Themen 6
D Java Objekt als Service in Runtime registrieren Allgemeine Java-Themen 1
Nero90 FileSystem Watcher Service Allgemeine Java-Themen 1
W Windows (Service) Hooking Allgemeine Java-Themen 8
C Threads ExecutorService shutdown abbrechen und service "starten" ? Allgemeine Java-Themen 3
A Framework für einen Web Service Allgemeine Java-Themen 6
M Webservices: WSDL Files ohne "Service" Element? Allgemeine Java-Themen 4
D ewig laufendes Javaprogramm ("Service") Allgemeine Java-Themen 17
D Executor Service nach getaner Arbeit beenden Allgemeine Java-Themen 3
Iron Monkey Listening for Print Service Status Changes Allgemeine Java-Themen 2
D Daemon bzw. Windows Service Allgemeine Java-Themen 5
O Unterschied zwischen ThreadPoolExecutor und Executor Service Allgemeine Java-Themen 7
J url-Service timeout einrichten? Allgemeine Java-Themen 10
G Java App als Service Allgemeine Java-Themen 2
J Eine Frage zu den Threads und Task Allgemeine Java-Themen 1
S Java-Task-Management-Tool für Windows und Mac selber programmieren Allgemeine Java-Themen 4
J Probleme exe-Start mit Task Scheduler Allgemeine Java-Themen 1
Neumi5694 Task-Name unter Windows Allgemeine Java-Themen 4
HarleyDavidson Tomcat VS Windows Scheduled Task Allgemeine Java-Themen 4
M Ant relative Pfade im Classpath vom Jar-Task Allgemeine Java-Themen 2
B Threads Main Thread warten auf abgebrochen Task warten lassen Allgemeine Java-Themen 25
N class Task extends TimerTask ohne Blockieren Allgemeine Java-Themen 15
A wie kann man Parameter an ein Task übergeben? Allgemeine Java-Themen 3
M Ant & javadoc-Task Allgemeine Java-Themen 1
R ANT Jar Task soll leere Verzeichnisse ignorieren..? Allgemeine Java-Themen 3
E Timer - gleichen Task mehrfach planen Allgemeine Java-Themen 2
A TimerTask - Task stoppen - timer.cancel() funktioniert nicht Allgemeine Java-Themen 8
G Task 1 sek nach letztem Tastendruck ausführen Allgemeine Java-Themen 2
M Java Programm als Windows XP - Task Allgemeine Java-Themen 9
D javaw.exe in task manager Allgemeine Java-Themen 6
B 24 Uhr Timer Task Allgemeine Java-Themen 5
C zyklisch Bsp. 1x pro Tag - morgens Task ausführen Allgemeine Java-Themen 14
J Timer-Objekt / Task-Zustand ermitteln Allgemeine Java-Themen 5

Ähnliche Java Themen

Neue Themen


Oben