同步java与javaFX线程

技术概述

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并在组里大佬的帮助下完成。

上一篇:JavaFX场景切换代码示例


下一篇:javaFX + MVVM桌面应用开发的解决方案