java使用线程来避免GUI死锁

时间:2016-03-30 19:40:19

标签: java multithreading canvas

我有一个项目,我在画布上绘制一个复杂的表单,取决于用户输入。

现在,计算和绘制表单需要相当长的时间,所以我将计算和绘图放在不同的线程上:

首先我计算所有边缘,然后将它们绘制到一个单独的画布上,然后将内容从单独的画布复制到屏幕画布。

计算代码

// Make 3 new threads. each thread calculates an edge.
    Thread thread1 = new Thread(new RunnableClass("L", nxt, edges, application));
    Thread thread2 = new Thread(new RunnableClass("R", nxt, edges, application));
    Thread thread3 = new Thread(new RunnableClass("B", nxt, edges, application));

    thread1.start();
    thread2.start();
    thread3.start();

    // The code waits here until all Threads are finished.
    try
    {
        thread1.join();
        thread2.join();
        thread3.join();
    } catch (InterruptedException ex)
    {
        Logger.getLogger(KochManager.class.getName()).log(Level.SEVERE, null, ex);
    }

然后我使用默认代码(使用GraphicsContext)绘制它,该代码有效,所以我不会在这里复制它。

然后我将内容复制到屏幕上:

SnapshotParameters params = new SnapshotParameters();
params.setFill(Color.TRANSPARENT);         
WritableImage image = hiddenCanvas.snapshot(params, null);
kochPanel.getGraphicsContext2D().drawImage(image, 0, 0);

但这是我的问题:无论我如何尝试,我总是最终使主线程死锁:如果我使用join(),它将等待线程结束。所以我想:我可以使用一个计时器来检查线程是否像这样完成:

timer.scheduleAtFixedRate(new TimerTask()
    {
        @Override
        public void run()
        {
            if (running == false) {
                SnapshotParameters params = new SnapshotParameters();
                params.setFill(Color.TRANSPARENT);         
                WritableImage image = hiddenCanvas.snapshot(params, null);
                kochPanel.getGraphicsContext2D().drawImage(image, 0, 0);
                application.setTextCalc( String.valueOf(timeStampCalc.toString()));
                application.setTextDraw(timeStampDraw.toString());
                application.setTextNrEdges(String.valueOf(kf.getNrOfEdges()));
            }
        }

    }, 0, 250);

当我发现定时器也在一个单独的线程上时。

所以我的问题是:我怎么能确保我等到绘制画布直到所有线程完成它们的东西,而没有阻塞/死锁主线程?

以下是GUI的外观示例,让您有个主意。 First example

2nd example

2 个答案:

答案 0 :(得分:1)

如果您不想使用join,可以使用监视器来控制线程执行

.sum()

您将使用要启动的线程数构造此对象。每个线程完成后,它将调用class ThreadJoiner` { private int numThreads; private int finishedThreads; public ThreadJoiner(int numThreads) { this.numThreads = numThreads; } public synchronized void updateFinishedThread() throws Exception { this.finishedThreads++; notify(); } public synchronized void awaitThreads() throws Exception { while(this.finishedThreads < this.numThreads) wait(); } } 。主线程在启动计算线程后应调用updateFinishedThread()

答案 1 :(得分:1)

根据您的代码段,您需要扩展TimerTask,提供您自己的实现。

在您的实现中,通过它的构造函数传递对负责访问running变量的对象的引用,然后通过此对象检查running