技术概述
JavaFX是单线程编程,所有对界面的操作都会交给唯一的线程Application Thread去处理。因此需要使用线程同步技术来避免异常。在实际应用中,我们房间是在开始游戏之前创建的,而游戏开始后又要处理传来的倒计时,图像,答案,聊天等数据并显示。这就需要java与javaFX线程的同步。
技术详述
使用Platform.runLater(...)实现多线程
Platform.runLater(...)是javafx.application包里自带的任务队列,原理是把你要执行的动作添加到类似队列中,等Application Thread线程空闲时处理,还是主线程(Application Thread)处理的,你的那个线程只是放在等待队列了。
使用时传入参数需要继承 runnable
我们在游戏中使用还加入了Timer来定时刷新数据。
代码结构如下
new Timer().schedule(new TimerTask() {
public void run() {
Platform.runLater(new Runnable() {
public void run() {
//子线程代码
}
});
}
}, 2000);
解决办法
Timer timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
synchronized (flag) {
if (flag) {
NewIo.send(ClientPost.Type.IM_ALIVE, new HashMap<>());
ServerReturn sr = NewIo.send(ClientGet.Type.STATE, new HashMap<>());
ArrayList<TernaryTuple<Integer, String, Boolean>> infolist = (ArrayList<TernaryTuple<Integer, String, Boolean>>) sr.args
.get("players");
ArrayList<BinaryTuple<String, String>> chatlist1 = (ArrayList<BinaryTuple<String, String>>) sr.args
.get("chat_list");
//ai++;
System.out.println("\n房主debug"+sr.args.toString());
Platform.runLater(new Runnable() {
@Override
public void run() {
// 更新JavaFX的主线程的代码放在此处
if (chatlist1 != null) {
for (BinaryTuple<String, String> cl : chatlist1) {
chatlist.add(cl.first + " :" + cl.second);
}
}
getchatlist.setItems(chatlist);
VBox vb = OwnerGame.rebuildleft(sr);
bp.setLeft(vb);
if(sr.args.get("guessed")!=null) {
answerlist.add("系统消息 "+sr.args.get("guessed")+"猜对了");
getanswerlist.setItems(answerlist);
}
if(sr.args.get("state").equals(State.CHOICE_TIME)) {
answerlist.clear();
answerlist.add("游戏开始本界面用于答题");
getanswerlist.setItems(answerlist);
}
if(sr.args.get("drawed") != null) {
dngdc.addDraw((List<TernaryTuple<Object, Object, List<BinaryDoubleTuple>>>) sr.args.get("drawed"));
}
bp.setCenter(rebuilddng(dngbp,sr));
}
});
} else {
NewIo.send(ClientPost.Type.QUIT, new HashMap<>());
}
}
}
}, 1,1000);
这种方法实质是使用 JavaFx 自带的一个任务队列, vafx.application.Platform.runlater。将 gui 更新任务放到队列里待更新, 传入参数需要继承 runnable。
用这种方法就能避免子线程直接更新gui造成的异常。
总结
使用Platform.runLater(...)构造工作队列即可实现java和javaFx的多线程同步。
参考
阅读JavaFx文档,查Stack Overflow并在组里大佬的帮助下完成。