JavaFX Canvas在频繁绘图时会停止更新

时间:2019-01-18 21:59:09

标签: java multithreading sorting javafx

我目前正在尝试编写一个程序以可视化排序算法。这包括绘制排序算法做出的每个步骤,对于冒泡排序之类的东西来说,这是很多的。我的问题是,当我运行代码时,它会随机停止在画布上显示算法。好像画布已分离,这使我相信它与JavaFX线程不安全有关。

起初,我尝试在同一线程上运行所有内容,这是一个糟糕的主意。排序循环阻止了对画布的更新,因此我只能看到结果。为了解决这个问题,我添加了一些Thread.sleep调用,当时这个问题浮出水面。它会工作一段时间,然后停止。我无法辨别是什么使它停止,并且它并非总是如此。对于具有更多步骤的算法来说,这种情况似乎更经常发生,并且如果我不在窗口中,几乎每次都会发生这种情况。发生这种情况的风险也随着我正在排序的数据集的大小而增加。

由于我的Thread.sleep调用不起作用,所以我决定搞乱线程,对此我非常陌生。我所做的是创建了一个可运行的可运行排序算法的可运行对象,然后在单独的线程中运行它。这减少了停止的频率,但没有完全减少。因此,我决定在sorting类中使另一个可运行,并在新线程中使用它在画布上进行绘制。再次有温和的改善,但未解决任何问题。我还尝试过用JavaFX.Tasks替换可运行对象,但是这没有什么区别。

这是在控制用户界面的Graphics类中。

public void start(Stage stage)
{
...
MyRunnable myRunnable = new MyRunnable(array, canvasContainer);
    Thread t = new Thread(myRunnable);
            t.start();
...
}

public class MyRunnable implements Runnable {
    private Comparable[] array;
    private CanvasContainer canvasContainer;

    public MyRunnable(Comparable[] array, CanvasContainer canvasContainer)
    {
        this.array = array;
        this.canvasContainer = canvasContainer;
    }

    @Override
    public void run()
    {
        Algorithms.bubbleSort(array, canvasContainer);
    }
}

CanvasContainer包含实际的画布以及两个整数x和y,用于存储我希望绘图开始的位置。重要的功能是绘制(画一条线)和删除(删除一条线)。

private void draw(Comparable a, int pos)
{
    canvas.getGraphicsContext2D().fillRect(x+SCALING*pos, y-SCALING*(int)a, SCALING, y);
}

private void delete(Comparable a, int pos)
{
    canvas.getGraphicsContext2D().setFill(Color.WHITE);
    canvas.getGraphicsContext2D().fillRect(x+SCALING*pos, y-SCALING*(int)a, SCALING, y);
    canvas.getGraphicsContext2D().setFill(Color.BLACK);

}

在我的Algorithms类中,我拥有所有不同的排序算法。 Bubblesort看起来像这样:

public static void bubbleSort(Comparable[] array, CanvasContainer cc)
{
    RunnableDouble myRunnable = new RunnableDouble(array, cc);
    Thread t = new Thread(myRunnable);
    int n = array.length;
    Comparable temp;
    while(n>0)
    {
        int newn = 0;
        for(int i = 1; i < n; i++)
        {
            if(array[i-1].compareTo(array[i]) > 0)
            {
                if(i%5==0)
                {
                    try {
                        Thread.sleep(0, 10000);
                        } catch (InterruptedException e) {
                        System.out.println("Error");
                        e.printStackTrace();
                        }
                }
                render(array[i], array[i-1], i, i-1, t, 100, 100000);
                newn = i;
            }
        }
        n = newn;
    }
    System.out.println("Färdig");
}

public static class RunnableDouble implements Runnable {
    private Comparable[] array;
    private CanvasContainer canvasContainer;

    public RunnableDouble(Comparable[] array, CanvasContainer canvasContainer)
    {
        this.array = array;
        this.canvasContainer = canvasContainer;
    }

    @Override
    public void run()
    {
        canvasContainer.delete(comp1, pos1);
        canvasContainer.delete(comp2, pos2);
        swap(array, pos1, pos2);
        canvasContainer.draw(comp2, pos1);
        canvasContainer.draw(comp1, pos2);
    }
}

private static void render(Comparable obj1, Comparable obj2, int index1, int index2, Thread t, int milli, int nano)
{
    while(t.isAlive())
    {

        try {
            Thread.sleep(milli, nano);
        } catch (InterruptedException e) {
            System.out.println("Error");
            e.printStackTrace();
        }
    }
    comp1 = obj1;
    comp2 = obj2;
    pos1 = index1;
    pos2 = index2;
    t.run();
}

对于小型数据集,冻结几乎没有任何问题,但是当我开始更高时,会遇到一些困难。如果我删除所有Thread.sleep调用,它将起作用,但是实际上我看不到排序的发生。我认为AnimationTimer可能是个好主意,但是我不知道如何实现仅在计时器触发时执行其步骤的排序算法。冻结后,我什么都不会出错,排序算法也就完成了。只是被卡住的画布。

编辑:我不同意这是椭圆形柱子的副本。虽然问题本质上是相同的,但是我看不到如何以相同的方式解决问题,因为排序不是连续的,就像每秒绘制相同的东西一样。

0 个答案:

没有答案