最近在尝试用javafx写一个简单的2D游戏,没有仔细学过FX和多线程,就闭门造车一次。
在制作过程中,发现当移动射击等操作越来越多、主页面图形变化逐渐丰富的时候,开始出现卡顿,最后有概率地出现Not on java FX Application问题,接着出现NullPointerException导致程序崩溃。逐步排查后发现可能是并发问题,之前一直没有考虑到并发,而是简单地使用Platform.runlater(new Runnable(public void run{}))来解决Not on FX application的问题。
虽然无法确认,但是并发问题有最大的嫌疑性。
为了验证并解决问题,受数据库的启发,写了一个执行中心类(HandleCenter)。
原理就是现将用数组存储起来,再进行批量依次操作。
我把每一次要进行图形变换的方法,例如
Platform.runLater(new Runnable() {
public void run() {
if(d.getId()==c.getId()) Main.pane.getChildren().remove(d);
}
});
直接看run的重写:在Main的pane里面直接删除Node d
解决:
把删除d的操作存储起来
PaintCenter.deleteBacth(d);
把所有的直接操作都替换成存储操作
然后在一个20L循环一次的线程里面执行批量操作:
private class MoveContro extends Thread{
public void run() {
while(true) {
PaintCenter.excecuteBatch();
}
}
}
最后发现问题被完美地解决了。
附上PaintCenter代码:
package com.Endemann.HandleCenter;
import java.util.ArrayList;
import com.Endemann.Controller.Controller01;
import com.Endemann.objects.Creature;
import javafx.scene.Node;
import javafx.scene.shape.Shape;
/**
* @author Endemann
* 2018/6/26
*
*/
public class PaintCenter {
private static ArrayList<Creature> addCreature = new ArrayList<Creature>();
private static ArrayList<Creature> deleteCreature = new ArrayList<Creature>();
private static ArrayList<Shape> addShape = new ArrayList<Shape>();
private static ArrayList<Shape> deleteShape = new ArrayList<Shape>();
private static ArrayList<Node> addNode = new ArrayList<Node>();
private static ArrayList<Node> deleteNode = new ArrayList<Node>();
public static void addBatch(Node node) {
addNode.add(node);
}
public static void addBatch(Creature c) {
addCreature.add(c);
}
public static void addBatch(Shape shape) {
addShape.add(shape);
}
public static void deleteBacth(Node node) {
deleteNode.add(node);
}
public static void deleteBacth(Creature c) {
deleteCreature.add(c);
}
public static void deleteBacth(Shape shape) {
deleteShape.add(shape);
}
public static void excecuteBatch() {
for(Creature c : addCreature)
Controller01.Paint(c);
for(Creature c : deleteCreature)
Controller01.Delete(c);
for(Shape s : addShape)
Controller01.Paint(s);
for(Shape s : deleteShape)
Controller01.Delete(s);
for(Node n : addNode)
Controller01.Paint(n);
for(Node n : deleteNode)
Controller01.Delete(n);
addCreature.clear();
deleteCreature.clear();
addShape.clear();
deleteShape.clear();
addNode.clear();
deleteNode.clear();
}
}
但是这样做有可能在遍历ArrayList的时候,发生了ArrayList的更新而导致ArrayList异常。对此,可以这样解决:
执行方法内创建一个局部的ArrayList,把batch里面的所有元素对其进行赋值,再对这个ArrayList进行遍历。那么在对这个ArratList遍历的过程中就不会出现ArrayList的并发异常了。最后对存储操作的List进行removeAll操作,删除已经遍历过的元素。
附上按此思路来写的移动批量处理代码:
package com.Endemann.HandleCenter;
import java.util.ArrayList;
/**
* @author Endemann
* 2018/6/26
*/
public class MoveCenter {
private static ArrayList<moveOperator> x = new ArrayList<moveOperator>();
private static ArrayList<moveOperator> y = new ArrayList<moveOperator>();
public static void X_addBatch(moveOperator mo) {
x.add(mo);
}
public static void Y_addBatch(moveOperator mo) {
y.add(mo);
}
public static void excecuteBatch() {
ArrayList<moveOperator> tempX = new ArrayList<moveOperator>(x);
for(moveOperator mo : tempX)
if(mo!=null && mo.getCreature() != null)mo.getCreature().setTranslateX(mo.getd());
ArrayList<moveOperator> tempY = new ArrayList<moveOperator>(y);
for(moveOperator mo : tempY)
if(mo!=null && mo.getCreature() != null)mo.getCreature().setTranslateY(mo.getd());
x.removeAll(tempX);
y.removeAll(tempY);
}
}