2015-03-30 2 views
0

Я делаю простую игру с игрой с JavaFX, и мне нужен поток для запуска, чтобы я мог проверить, жив или нет противник или игрок. Моя проблема не начинается и не использует поток, но останавливается, когда окно закрывается.Запуск темы в JavaFX

Я сделал образец класса, чтобы продемонстрировать, что я имею в виду.

private Thread thread; 
private boolean running; 
private Stage window; 

public void run() { 
    while (running) { 
     System.out.println("Hello"); 
    } 
    stopThread(); 
} 

public synchronized void startThread() { 
    running = true; 
    thread = new Thread(this, "Monitor"); 
    thread.start(); 
} 

public synchronized void stopThread() { 
    running = false; 
    try { 
     thread.join(); 
    } catch (InterruptedException e) { 
     e.printStackTrace(); 
    } 
} 

public void start(Stage stage) throws Exception { 
    window = new Stage(); 
    window = stage; 
    Pane layout = new Pane(); 
    Scene scene = new Scene(layout); 

    window.setOnCloseRequest(e -> { 
     e.consume(); 
     close(); 
    }); 
    window.setScene(scene); 
    window.show(); 
} 

public void close() { 
    window.close(); 
    stopThread(); 
} 

public static void main(String[] args) { 
    Things things = new Things(); 
    things.startThread(); 
    launch(args); 
} 

Когда я запускаю его, «Hello» печатает непрерывно, но когда я пытаюсь закрыть его, поток продолжает работать и eclispe переходит в режим отладки, говоря:

Thread [JavaFX Application Thread] (Suspended (exception NullPointerException)) QuantumToolkit.runWithoutRenderLock(Supplier<T>) line: not available GlassWindowEventHandler.handleWindowEvent(Window, long, int) line: not available WinWindow(Window).handleWindowEvent(long, int) line: not available WinWindow(Window).notifyClose() line: not available WinApplication._runLoop(Runnable) line: not available [native method] WinApplication.lambda$null$145(Runnable) line: not available 2091156596.run() line: not available Thread.run() line: not available

Я смотрел вокруг моего кода и не может найти ничего, что является нулевым.

+0

Вы выходящим из приложения при закрытии окна, или есть другие окна, показывающие? –

ответ

0

Возможно, вы столкнулись с ситуацией в режиме ожидания/тупика.

В вашем методе запуска, после того, как цикл while завершается, ваш поток пытается остановить себя, а затем попытаться дождаться, когда он умрет. Это синхронизированный метод, означающий, что только один поток может запускать его за раз.

Когда вы вызываете его из основного метода и пытаетесь остановить поток, он попытается присоединиться к потоку и дождаться его смерти. Поскольку этот первый вызов уже выполняет ваш метод stopThread, когда цикл while завершается в вашем методе запуска, он будет сидеть там и блокироваться, а не заставлять поток умирать. Поскольку нить не умирает, первый вызов stopThread не завершится.

Вы можете удалить stopThread из своего метода run, и он должен вести себя лучше, хотя из того, что вы разместили здесь, похоже, что лучший дизайн может быть в порядке. Мне было бы любопытно, если мое предложение будет работать.

При дальнейшей проверке у вас также возникла проблема со статическими/нестатическими данными в ваших потоках.

Я предполагаю, что ваш Things класс реализует Runnable, таким образом, когда вы передаете его в Thread конструктора и запустить его, он получает свою собственную копию стека, и это собственные локальные переменные. Когда вы вызываете stopThread в свой метод close, вы не устанавливаете правильную переменную running (это не та, которую видит поток). Если вы сделаете это поле static и удалите код thread.join..., он будет вести себя правильно.

EDIT: Как лучший выбор дизайна, было бы разумнее использовать возможности, которые вы получаете с Threads/Runnables, и то, как они обычно управляются в приложении (для получения дополнительной информации, поскольку вы используете JFX, вы следует посмотреть Службы/Задачи)

Разделите реализацию потока и основной класс и используйте функцию самого потока, чтобы остановить ее.

Возьмем такой пример, который значительно чище, и работает точно так же:

public class Things implements Runnable { 
    private volatile boolean running; // this indicates it's running 

    public Things() { 
     running = true; // thread is running when it's created (though not really until "run" is called) 
    } 

    // called from start() 
    public void run() { 
     while (running) { 
      System.out.println("Hello"); 
     } 
    } 

    // call this method to stop the thread, from outside on the object reference 
    public synchronized void stop() { 
     running = false; 
    } 
} 

В отдельном классе Test/Application, вы можете манипулировать Things ссылку, чтобы сделать то, что вы хотите, без сохранения ссылки к фактической теме.

public class Test extends Application { 
    private Stage window; 
    private static Things things; 

    public void start(Stage stage) throws Exception { 
     window = new Stage(); 
     window = stage; 
     Pane layout = new Pane(); 
     Scene scene = new Scene(layout); 

     window.setOnCloseRequest(new EventHandler<WindowEvent>() { 
      @Override 
      public void handle(WindowEvent windowEvent) { 
       close(); 
      } 
     }); 
     window.setScene(scene); 
     window.show(); 
    } 


    // on close 
    public void close() { 
     window.close(); 
     things.stop(); // stop the things object directly 
    } 

    public static void main(String[] args) { 
     things = new Things(); // create new runnable 
     Thread t = new Thread(things,"Monitor"); // create thread 
     t.start(); // start thread 
     launch(args); // launch app 
    } 
} 
+0

Перекрестная ссылка с вашим обновлением; не означал, чтобы украсть ваш ответ ... –

+0

Большое вам спасибо, это сработало! –

+0

@James_D не беспокоится, я работал над предлагаемым дизайном, когда вы отправляли, чем больше информации у OP, тем лучше. –

1

Предполагая Things это имя класса, который вы показали, что вы не останавливая правильную нить.

При вызове launch(args), то FX Toolkit создает экземпляр класса приложения (который я предполагаю, что это Things), создает Stage и передает Stage к start(...) методе экземпляра приложения (выполнение этого метода на FX Тема приложения).

Итак, вы создаете один экземпляр и начинаете поток на этом экземпляре. Затем инструментарий FX создает второй экземпляр, и именно в этом случае вы пытаетесь остановить поток. Таким образом, вы останавливаете другой поток на тот, который вы начали.

Чтобы исправить это, чтобы удалить строки

Things things = new Things(); 
things.startThread(); 

из метода main(...), а просто добавить

this.startThread(); 

в начале вашего метода start(...).

Кроме того, как указывает @RyanJ, вы зашли в тупик, поскольку у вас есть один поток, ожидающий завершения второго потока, и оба пытаются выполнить тот же метод synchronized. Кроме того, вы должны объявить running, как volatile, так как он доступен из нескольких потоков:

private volatile boolean running ; 

Так это работает:

import javafx.application.Application; 
import javafx.scene.Scene; 
import javafx.scene.layout.Pane; 
import javafx.stage.Stage; 


public class Things extends Application implements Runnable { 

    private Thread thread; 
    private volatile boolean running; 
    private Stage window; 

    @Override 
    public void run() { 
     while (running) { 
      System.out.println("Hello"); 
      try { 
       Thread.sleep(1000); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     } 
//  stopThread(); 
     running = false ; 
    } 

    public synchronized void startThread() { 
     running = true; 
     thread = new Thread(this, "Monitor"); 
     thread.start(); 
    } 

    public synchronized void stopThread() { 
     running = false; 
     try { 
      thread.join(); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 

    @Override 
    public void start(Stage stage) throws Exception { 
     startThread(); 
     window = stage; 
     Pane layout = new Pane(); 
     Scene scene = new Scene(layout); 

     window.setOnCloseRequest(e -> { 
      e.consume(); 
      close(); 
     }); 
     window.setScene(scene); 
     window.show(); 
    } 

    public void close() { 
     window.close(); 
     stopThread(); 
    } 

    public static void main(String[] args) { 
     launch(args); 
    } 
} 

Один последний момент: если это единственное открытое окно, то JVM выйдет, когда вы закроете окно, если нет не-daemon потоков. Таким образом, вы можете просто позволить JVM убить ваш поток, сделав его потоком демона. Если это работает для реального приложения, вы можете сделать любую зачистку вам необходимо переопределением stop метода:

import javafx.application.Application; 
import javafx.scene.Scene; 
import javafx.scene.layout.Pane; 
import javafx.stage.Stage; 


public class Things extends Application implements Runnable { 

    private Thread thread; 
    private volatile boolean running; 
    private Stage window; 

    @Override 
    public void run() { 
     while (running) { 
      System.out.println("Hello"); 
      try { 
       Thread.sleep(1000); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     } 
//  stopThread(); 
     running = false ; 
    } 

    public synchronized void startThread() { 
     running = true; 
     thread = new Thread(this, "Monitor"); 

     thread.setDaemon(true); 

     thread.start(); 
    } 


    @Override 
    public void start(Stage stage) throws Exception { 
     startThread(); 
     window = stage; 
     Pane layout = new Pane(); 
     Scene scene = new Scene(layout); 

     window.setScene(scene); 
     window.show(); 
    } 



    public static void main(String[] args) { 
     launch(args); 
    } 
} 
+0

Благодарим вас за быстрый ответ. –

Смежные вопросы