JavaFX ExecutorService丢失任务

时间:2016-12-04 07:43:38

标签: controller javafx-8

我正在学习JavaFX,并且遇到了运行任务的绊脚石。我使用NetBeans调试器跟踪了任务的进度,发现ConfigModelSansUpdateTask明确运行。

当应用程序到达configModelWorkerSansUpdates.setOnSucceeded时,此结果(存储在configModel中)会以null变为NullPointerException,并抛出NullPointerException

阅读了/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package swmuapp; import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; import jaxb.TaskModel; /** * * @author Shaun Connelly-Flynn */ public class SWMUApp extends Application { public SWMUApp() { } @Override public void start(Stage stage) throws Exception { FXMLLoader centralSeceneLoader = new FXMLLoader(getClass().getResource("forms/CentralScene.fxml")); Parent centralRoot = (Parent) centralSeceneLoader.load(); Scene scene = new Scene(centralRoot); stage.setScene(scene); stage.show(); } /** * @param args the command line arguments */ public static void main(String[] args) { launch(args); } 上的帖子后,我对于为什么我的任务结果被取消没有明智之处。没有什么可以做到这一点,因为当我使用它时,我明显得到一个结果然后变为null:

任务即将返回结果的快照:

NetBeans

当我来使用结果时:

Null result

帮助!

SWMUApp.java

package swmuapp.controllers;

import java.net.URL;
import java.util.ResourceBundle;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.concurrent.WorkerStateEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Tab;
import jaxb.TaskModel;
import swmuapp.SWMUApp;
import swmuapp.init.ConfigModelWorkerSansUpdates;
import swmuapp.init.LocationModelWorker;
import swmuapp.models.ConfigModel;
import swmuapp.models.LocationModel;

/**
 * FXML Controller class
 *
 * @author Shaun Connelly-Flynn
 */

public class CentralSceneController implements Initializable {

    @FXML
    private Tab sessionTab;
    @FXML
    private Tab tractionTab;
    @FXML
    private Tab locationTab;
    @FXML
    private Tab swmuTab;
    @FXML
    private Tab exportTab;

    private final TaskModel taskModel;

    // Models
    private ConfigModel configModel;
    private LocationModel locationModel;

    // Child Controllers
    @FXML
    private ImportPaneController importPaneController;

    @FXML
    private SWMUPaneController swmuPaneController;

    @FXML
    private SessionPaneController sessionPaneController;

    public CentralSceneController() {
        this.taskModel = new TaskModel();
    }

    /**
     * Initialises the controller class.
     *
     * @param url
     * @param rb
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // Give a reference of this controller to the child controllers
        importPaneController.setParentController(this);

        // Now subimt the configModel task
        ConfigModelWorkerSansUpdates configModelWorkerSansUpdates = new ConfigModelWorkerSansUpdates(taskModel);

        configModelWorkerSansUpdates.setOnSucceeded((WorkerStateEvent event) -> {
            System.out.println(event.getEventType().toString());

            // Get the location model
            Future<LocationModel> locationFuture = taskModel.submitTask(new LocationModelWorker(configModel, taskModel));

            try {
                locationModel = locationFuture.get();
            } catch (InterruptedException | ExecutionException ex) {
                Logger.getLogger(SWMUApp.class.getName()).log(Level.SEVERE, null, ex);
            }

            // Notify the controllers that we're ready
            sessionPaneController.setConfigModel(configModel);
            sessionPaneController.setLocationModel(locationModel);
        });

        configModelWorkerSansUpdates.setOnFailed(p -> System.out.println(p.toString()));
        configModelWorkerSansUpdates.setOnCancelled(p -> System.out.println(p.toString()));

        // Fetch the config model
        Future<ConfigModel> configFuture = taskModel.submitTask(configModelWorkerSansUpdates);

        try {
            configModel = configFuture.get();
        } catch (InterruptedException | ExecutionException ex) {
            Logger.getLogger(SWMUApp.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

}

CentralSceneController.java

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package jaxb;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import javafx.concurrent.Task;

/**
 *
 * @author Shaun Connelly-Flynn
 */
public class TaskModel {
    private final ExecutorService service;

    public TaskModel() {
        this.service = Executors.newCachedThreadPool();
    }

    public Future submitTask(Callable task) {
        return service.submit(task);
    }

    public Future submitTask(Task task) {
        return service.submit(task);
    }

    public List<Future> submitTasks(List<Callable> tasks) {
        List<Future> futureList = new ArrayList<>();

        for(Callable task : tasks) {
            futureList.add(submitTask(task));
        }

        return futureList;
    }
}

TaskModel.java

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package swmuapp.init;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import javafx.concurrent.Task;
import jaxb.Binder;
import jaxb.TaskModel;
import jaxb.bundles.QueryBundle;
import jaxb.bundles.ResultsBundle;
import jaxb.query.XQuery;
import swmuapp.config.jaxb.JaxbConfig;
import swmuapp.config.jaxb.JaxbDatabases;
import swmuapp.models.ConfigModel;

/**
 *
 * @author Shaun Connelly-Flynn
 */
public class ConfigModelWorkerSansUpdates extends Task<ConfigModel> {

    private final TaskModel taskModel;

    public ConfigModelWorkerSansUpdates(TaskModel taskModel) {
        this.taskModel = taskModel;
    }

    @Override
    protected ConfigModel call() throws Exception {        
        // Get the list of active databases
        QueryBundle databaseBundle = new QueryBundle(
                new XQuery("SWMUDB", "<databases>{databases/database}</databases>"),
                JaxbDatabases.class,
                "src/swmuapp/schema/DatabaseSchema.xsd");

        // Now read the configuration
        QueryBundle configBundle = new QueryBundle(
                new XQuery("SWMUDB", "configs/config"),
                JaxbConfig.class,
                "src/swmuapp/schema/ConfigSchema.xsd");

        // Submit the tasks
        Future<ResultsBundle> dbTask = taskModel.submitTask(new Binder(databaseBundle, taskModel));
        Future<ResultsBundle> configTask = taskModel.submitTask(new Binder(configBundle, taskModel));

        // Create empty objects in case of failure!
        JaxbDatabases dbResult = new JaxbDatabases();
        JaxbConfig configResult = new JaxbConfig();

        try {
            // Now fetch the list of databases
            dbResult = (JaxbDatabases) dbTask.get().getResult();

            // Fetch the configuration
            configResult = (JaxbConfig) configTask.get().getResult();
        } catch (ExecutionException ex) {
            setException(ex);
            throw ex;
        }

        // Now put it all together
        ConfigModel configModel = new ConfigModel(dbResult);

        // And give it back!
        return configModel;
    }
}

ConfigModelWorkerSansUpdates.java

package swmuapp.controllers;

import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ListView;
import javafx.scene.control.TableView;
import swmuapp.models.ConfigModel;
import swmuapp.models.LocationModel;

/**
 * FXML Controller class
 *
 * @author Shaun Connelly-Flynn
 */
public class SessionPaneController implements Initializable {

    @FXML
    private ListView<String> dbListView;
    @FXML
    private TableView<?> detailedTableView;

    private CentralSceneController controller;

    private ConfigModel configModel;
    private LocationModel locationModel;

    /**
     * Initializes the controller class.
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {

    }

    public void setConfigModel(ConfigModel configModel) {
        if (this.configModel == null) {
            this.configModel = configModel;
        } else {
            throw new IllegalArgumentException("SessionPaneController::setConfigModel -- config model already set!");
        }

        dbListView.setItems(configModel.getDatabases());
    }

    public void setLocationModel(LocationModel locationModel) {
        this.locationModel = locationModel;
    }

    protected void setParentController(CentralSceneController controller) {
        this.controller = controller;
    }
}

SessionPaneController.java

java.util.concurrent.ExecutionException: java.lang.NullPointerException
    at java.util.concurrent.FutureTask.report(FutureTask.java:122)
    at java.util.concurrent.FutureTask.get(FutureTask.java:192)
    at swmuapp.controllers.CentralSceneController.lambda$initialize$0(CentralSceneController.java:84)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.event.Event.fireEvent(Event.java:198)
    at javafx.concurrent.EventHelper.fireEvent(EventHelper.java:219)
    at javafx.concurrent.Task.fireEvent(Task.java:1356)
    at javafx.concurrent.Task.setState(Task.java:723)
    at javafx.concurrent.Task$TaskCallable.lambda$call$500(Task.java:1434)
    at com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(PlatformImpl.java:294)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NullPointerException
    at swmuapp.init.LocationModelWorker.call(LocationModelWorker.java:35)
    at swmuapp.init.LocationModelWorker.call(LocationModelWorker.java:22)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    ... 1 more

Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
    at swmuapp.controllers.SessionPaneController.setConfigModel(SessionPaneController.java:49)
    at swmuapp.controllers.CentralSceneController.lambda$initialize$0(CentralSceneController.java:90)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.event.Event.fireEvent(Event.java:198)
    at javafx.concurrent.EventHelper.fireEvent(EventHelper.java:219)
    at javafx.concurrent.Task.fireEvent(Task.java:1356)
    at javafx.concurrent.Task.setState(Task.java:723)
    at javafx.concurrent.Task$TaskCallable.lambda$call$500(Task.java:1434)
    at com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(PlatformImpl.java:294)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
    at java.lang.Thread.run(Thread.java:745)

堆栈:

public function store(Request $request){
 $input = Input::all();
 $servicedata = $input['service_id'];
 $employeedata = $input['emp_id'];
 $allOrder = array();

 foreach ($employeedata as $key => $val) {
    $detailorder = new DetailOrder;
    $detailorder->employee_id = $input['emp_id'][$key];
    $detailorder->mandays = $input['mandays'][$key];

    foreach ($servicedata as $key => $val) {

        $detailorder->sales_order_id = $request->sales_order_id;
        $detailorder->serivce_id = $input['service_id'][$key];
        $detailorder->order_type = $input['order_type'][$key];
        $detailorder->select_plan = $input['select_plan'][$key];
        $detailorder->qty = $input['qty'][$key];
        $detailorder->unit_price = $input['unit_price'][$key];
        $detailorder->note = $input['note'][$key];
    }
    $detailorder->save();
 }
}

2 个答案:

答案 0 :(得分:1)

我怀疑调度决策的某些组合导致onSucceeded处理程序在Future.get返回之前执行,因此尚未分配CentralSceneController.configModel

在从同一个线程发布任务后直接使用Future.get()是不行的,因为它强制当前线程等待任务完成,并且可能已经用这段时间直接运行ask。如果不应该阻止当前线程,比如javafx应用程序线程,情况会更糟。

解决这个问题应该很容易:

只需从onSucceeded处理程序中检索值,因为在应用程序线程上任务完成后触发此处理程序并使用此方法检索该值不会阻止应用程序线程。 / p>

以下代码假定LocationModelWorker扩展Task<LocationModel>

重写LocationModelWorkerFuture<ConfigModel>作为constuctor参数而不是ConfigModel参数。在get方法启动之前,请确保Futurecall被调用(例如,不要从构造函数中调用它)。

configModelWorkerSansUpdates.setOnSucceeded((WorkerStateEvent event) -> {
    sessionPaneController.setConfigModel(configModelWorkerSansUpdates.getValue());
});

...

Future<ConfigModel> configFuture = taskModel.submitTask(configModelWorkerSansUpdates);

LocationModelWorker locationModelWorker = new LocationModelWorker(configFuture, taskModel);
locationModelWorker.setOnSucceeded(evt -> {
    sessionPaneController.setLocationModel(locationModelWorker.getValue());
});

...

taskModel.submitTask(locationModelWorker);

如果两个任务成功完成,您可能需要稍微调整一下以防止setLocationModelsetConfigModel

答案 1 :(得分:0)

我从另一个帖子中找到了更多信息! TaskRunnable而非Callable

的子类

如本文所述:

FX Task, returning a value always returns null

Javadoc表明:

  

Future的get方法在成功完成后将返回null。

请参阅here以获取上述摘录的Javadoc。

我错误地假设TaskCallable的实现,而事实并非如此。

因此,Future.get()在成功完成时将返回null,因为Runnable没有返回结果。