【问题标题】:Kill a Thread in the PropertyListener (JavaFX8)在属性监听器中杀死一个线程 (JavaFX 8)
【发布时间】:2014-10-08 17:45:03
【问题描述】:

我知道 Java 的实际模型是用于协作线程的,并且它强制线程死亡是不可能发生的。

由于Thread.stop() 已被弃用(出于上述原因)。我试图通过 BooleanProperty 侦听器停止线程。

这是 MCVE:

TestStopMethod.java

package javatest;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ObservableValue;
public class TestStopMethod extends Thread {
    private BooleanProperty amIdead = new SimpleBooleanProperty(false);
    public void setDeath() {
        this.amIdead.set(true);
    }

    @Override
    public void run() {
        amIdead.addListener((ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) -> {
            System.out.println("I'm dead!!!");
            throw new ThreadDeath();
        });
        for(;;);
    }
}

WatchDog.java

package javatest;

import java.util.TimerTask;

public class Watchdog extends TimerTask {
    TestStopMethod watched;
    public Watchdog(TestStopMethod target) {
        watched = target;
    }
    @Override
    public void run() {
        watched.setDeath();
        //watched.stop(); <- Works but this is exactly what I am trying to avoid
        System.out.println("You're dead!");
    }

}

Driver.java

package javatest;

import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Driver {

    public static void main(String[] args) {
        try {
            TestStopMethod mythread = new TestStopMethod();
            Timer t = new Timer();
            Watchdog w = new Watchdog(mythread);
            t.schedule(w, 1000);
            mythread.start();
            mythread.join();
            t.cancel();
            System.out.println("End of story");
        } catch (InterruptedException ex) {
            Logger.getLogger(Driver.class.getName()).log(Level.SEVERE, null, ex);
        }

    }
}

【问题讨论】:

  • 您认为哪个线程会抛出ThreadDeath 错误?
  • 我想,如果我向属性添加一个侦听器并更改其在 WatchDog 中的值,它会引发该异常。现在的代码会继续运行,因此永远不会调用 mythread.join()
  • 它将从更改属性的线程中抛出,该线程是支持计时器实例的线程。 (如果您考虑一下,基本上不可能安排在任意线程上调用侦听器。)我认为您将拥有该方法的对象与执行该方法的线程混淆了。

标签: java multithreading javafx javafx-8 preemptive


【解决方案1】:

如果您更改属性值,则在更改属性的同一线程上调用侦听器(只需考虑您将/可以如何实现属性类)。因此,在您的示例中,ThreadDeath 错误是从支持 Timer 实例的线程抛出的,这并不是您真正想要的。

从外部(到该线程)终止线程的正确方法是设置一个标志,然后在线程的实现中定期轮询该标志。这实际上比听起来更棘手,因为必须从多个线程访问标志,因此必须正确同步对它的访问。

幸运的是,有一些实用程序类可以帮助解决这个问题。例如,FutureTask 包装 RunnableCallable 并提供 cancel()isCancelled() 方法。如果您使用的是 JavaFX,那么 javafx.concurrent API 提供了一些 CallableRunnable 的实现,并且还提供了专门用于在 FX 应用程序线程上执行代码的功能。查看documentation for javafx.concurrent.Task 中的一些示例。

因此,例如,您可以这样做:

package javatest;
public class TestStopMethod implements Runnable {

    @Override
    public void run() {
        try {
            synchronized(this) {
                for(;;) {
                    wait(1); 
                }
            }
        } catch (InterruptedException exc) {
            System.out.println("Interrupted");
        }
    }
}

Watchdog.java:

package javatest;

import java.util.TimerTask;
import java.util.concurrent.Future;

public class Watchdog extends TimerTask {
    Future<Void> watched;
    public Watchdog(Future<Void> target) {
        watched = target;
    }
    @Override
    public void run() {
        watched.cancel(true);
        //watched.stop(); <- Works but this is exactly what I am trying to avoid
        System.out.println("You're dead!");
    }
}

Driver.java:

package javatest;

import java.util.*;
import java.util.concurrent.FutureTask;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Driver {

    public static void main(String[] args) {
        try {
            FutureTask<Void> myTask = new FutureTask<>(new TestStopMethod(), null);
            Timer t = new Timer();
            Watchdog w = new Watchdog(myTask);
            t.schedule(w, 1000);
            Thread mythread = new Thread(myTask);
            mythread.start();
            mythread.join();
            t.cancel();
            System.out.println("End of story");
        } catch (InterruptedException ex) {
            Logger.getLogger(Driver.class.getName()).log(Level.SEVERE, null, ex);
        }

    }
}

在 JavaFX 应用程序中,您可能会这样做。请注意,如果您尝试在没有运行 FX 应用程序线程的情况下执行此操作,事情会变得很糟糕,因为 FX Task 中的 cancelled 标志必须在该线程上更新。

package javatest;

import javafx.concurrent.Task;

public class TestStopMethod extends Task<Void> {

    @Override
    public Void call() {
        System.out.println("Calling");
        while (true) {
            if (isCancelled()) {
                System.out.println("Cancelled");
                break ;
            }
        }
        System.out.println("Exiting");
        return null ;
    }
}

Watchdog.java:

package javatest;

import java.util.TimerTask;

import javafx.concurrent.Task;

public class Watchdog extends TimerTask {
    Task<Void> watched;
    public Watchdog(Task<Void> target) {
        watched = target;
    }
    @Override
    public void run() {
        watched.cancel();
        //watched.stop(); <- Works but this is exactly what I am trying to avoid
        System.out.println("You're dead!");
    }

}

驱动程序.java

package javatest;

import java.util.Timer;
import java.util.logging.Level;
import java.util.logging.Logger;

import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.concurrent.Worker;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class Driver extends Application {

    @Override
    public void start(Stage primaryStage) {
        try {

            TextArea console = new TextArea();
            BorderPane root = new BorderPane(console);
            Scene scene = new Scene(root, 600, 400);
            primaryStage.setScene(scene);
            primaryStage.show();

            Task<Void> myTask = new TestStopMethod();
            Timer t = new Timer();
            Watchdog w = new Watchdog(myTask);
            t.schedule(w, 1000);
            Thread mythread = new Thread(myTask);
            mythread.setDaemon(true);

            myTask.stateProperty().addListener((obs, oldState, newState) -> {
                console.appendText("State change "+oldState+" -> "+newState+"\n");
                if (oldState == Worker.State.RUNNING) {
                    t.cancel();
                    console.appendText("End of Story\n");
                }
            });
            mythread.start();

        } catch (Exception ex) {
            Logger.getLogger(Driver.class.getName()).log(Level.SEVERE, null, ex);
        }

    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-29
    • 1970-01-01
    相关资源
    最近更新 更多