TableView不从线程更新新信息

时间:2017-06-01 02:23:20

标签: multithreading javafx tableview

我为我遇到的问题做了一个小的工作示例程序。请记住,我的真实程序更复杂,所有这些类都是必需的,即使它在这个小样本中看起来过于庞大

主类:

public class PingTest extends Application {

private static final ArrayList<PingThread> THREADS = new ArrayList();

@Override
public void start(Stage primaryStage) {

    AnchorPane root = new AnchorPane();
    TableView<Map.Entry<String, Ping>> tblPing = new TableView();
    TableColumn<Map.Entry<String, Ping>, String> colName = new TableColumn("Name");
    TableColumn<Map.Entry<String, Ping>, String> colTime = new TableColumn("Time");
    TableColumn<Map.Entry<String, Ping>, String> colDifference = new TableColumn("Difference");

    colName.setCellValueFactory((TableColumn.CellDataFeatures<Map.Entry<String, Ping>, String> p) -> new SimpleStringProperty(p.getValue().getKey()));
    colTime.setCellValueFactory((TableColumn.CellDataFeatures<Map.Entry<String, Ping>, String> p) -> new SimpleStringProperty(Integer.toString(p.getValue().getValue().getTime())));
    colDifference.setCellValueFactory((TableColumn.CellDataFeatures<Map.Entry<String, Ping>, String> p) -> new SimpleStringProperty(Integer.toString(p.getValue().getValue().getDifference())));

    ObservableList<Map.Entry<String, Ping>> items = FXCollections.observableArrayList(Pings.getPings().entrySet());
    tblPing.setItems(items);

    Scene scene = new Scene(root, 300, 750);

    // Align to pane
    AnchorPane.setTopAnchor(tblPing, 0.0);
    AnchorPane.setLeftAnchor(tblPing, 0.0);
    AnchorPane.setRightAnchor(tblPing, 0.0);
    AnchorPane.setBottomAnchor(tblPing, 0.0);

    tblPing.getColumns().addAll(colName, colTime, colDifference);
    root.getChildren().add(tblPing);

    // Setting primary Stage
    primaryStage.setOnCloseRequest(e -> {
        THREADS.forEach(t -> t.interrupt());
    });
    primaryStage.setTitle("Ping List");
    primaryStage.setScene(scene);
    primaryStage.show();

}

public static void main(String[] args) {
    // Some examples
    String[] servers = {"www.google.com", "www.bing.com", "www.yahoo.com", "www.stackoverflow.com"};

    // Start Threads
    for (String server : servers) {
        PingThread pingThread = new PingThread(server);
        pingThread.start();
        THREADS.add(pingThread);
    }
    launch(args);
}

}

助手类

public class Ping {

private int difference;
private int time;
private final String url;

public Ping(String url,int time) {
    this.url = url;
    this.time = time;
    difference = 0;
}

public int getDifference() {
    return difference;
}

public int getTime() {
    return time;
}

public void setTime(int time) {
    difference = time - this.time;
    this.time = time;
}

public String getUrl() {
    return url;
}

}

线程

public class PingThread extends Thread {

private final String SERVER;
private int ping = 0;

public PingThread(String server) {
    SERVER = server;
}

@Override
public void run() {
    try {
        while (true) {
            // Ping, update then sleep
            ping();
            Pings.update(SERVER, ping);
            Thread.sleep(1000);
        }
    } catch (InterruptedException ex) {
        // Used to end thread
    }
}

public void ping() {
    try {
        // Sendinging request
        InetAddress host = InetAddress.getByName(SERVER);
        long tm = System.nanoTime();
        Socket so = new Socket(host, 80);
        so.close();
        ping =(int)( (System.nanoTime() - tm) / 1000000L);
    } catch (IOException ex) {
    }

}

}

数据容器

public class Pings {

private static final ConcurrentHashMap<String,Ping> PINGS=  new ConcurrentHashMap();

public static synchronized void update(String server, int time) {
    if (PINGS.containsKey(server)) {
        PINGS.get(server).setTime(time);
    } else {
        PINGS.put(server, new Ping(server,time));
    }
}
public static synchronized ConcurrentHashMap<String,Ping> getPings(){
    return PINGS;
}
}

在我的更大实现中,GUI表没有更新为新值,就像在这个小例子中一样。将值绑定到TableView时我做错了什么?

1 个答案:

答案 0 :(得分:0)

此代码存在许多问题。

最直接的是呼叫

FXCollections.observableArrayList(Pings.getPings().entrySet());

创建一个新的可观察列表,并将Pings.getPings().entrySet()中的所有内容添加到其中。但随后修改用于构造它的集合将不会修改可观察列表,因此表格不会更改。您需要将可观察列表存储在模型(Pings)中并直接更新。

您还需要在FX应用程序线程上进行更新,并且您需要在Ping类中使用JavaFX属性,以便表格可以观察到这些值的更改。

所以:

public class Ping {

    private final ReadOnlyIntegerWrapper difference = new ReadOnlyIntegerWrapper();
    private final ReadOnlyIntegerWrapper time = new ReadOnlyIntegerWrapper();
    private final String url;

    public Ping(String url,int time) {
        this.url = url;
        setTime(time);
        difference.set(0);
    }

    public ReadOnlyIntegerProperty differenceProperty() {
        return difference.getReadOnlyProperty() ;
    }

    public final int getDifference() {
        return differenceProperty().get();
    }

    public ReadOnlyIntegerProperty timeProperty() {
        return time ;
    }

    public final int getTime() {
        return timeProperty().get();
    }

    public void setTime(int time) {
        difference.set(time - getTime());
        this.time.set(time);
    }

    public String getUrl() {
        return url;
    }

}

必须在FX应用程序线程上更改数据,因此您的数据容器也可以是单线程的:

public class Pings {

    private static final Map<String,Ping> PINGS=  new HashMap<>();
    private static final ObservableList<Ping> pingList = FXCollections.observableArrayList();

    public static void update(String server, int time) {
        if (PINGS.containsKey(server)) {
            PINGS.get(server).setTime(time);
        } else {
            Ping ping = new Ping(server, time);
            pingList.add(ping);
            PINGS.put(server, ping);
        }
    }
    public static ObservableList<Ping> getPings(){
        return pingList;
    }

}

现在线程确实:

public class PingThread extends Thread {

    private final String SERVER;

    public PingThread(String server) {
        SERVER = server;
    }

    @Override
    public void run() {
        try {
            while (true) {
                // Ping, update then sleep
                final int ping = ping();
                // again, not exactly sure how you want to handle exceptions...
                if (ping >= 0) {
                    Platform.runLater(() -> Pings.update(SERVER, ping));
                }
                Thread.sleep(1000);
            }
        } catch (InterruptedException ex) {
            // Used to end thread
        }
    }

    public int ping() {
        try {
            // Sendinging request
            InetAddress host = InetAddress.getByName(SERVER);
            long tm = System.nanoTime();
            Socket so = new Socket(host, 80);
            so.close();
            return (int)( (System.nanoTime() - tm) / 1000000L);
        } catch (IOException ex) {
            return -1 ; // or handle exception?
        }

    }

}

最后将表格设置为:

TableView<Ping> tblPing = new TableView<>();
TableColumn<Ping, String> colName = new TableColumn<>("Name");
TableColumn<Ping, Number> colTime = new TableColumn<>("Time");
TableColumn<Ping, Number> colDifference = new TableColumn<>("Difference");

colName.setCellValueFactory(p -> new SimpleStringProperty(p.getValue()));
colTime.setCellValueFactory(p -> p.getValue().timeProperty());
colDifference.setCellValueFactory(p -> p.getValue().differenceProperty());

tblPing.getColumns().addAll(colName, colTime, colDifference);

tblPing.setItems(Pings.getPings());