Jordumus
Jordumus

Reputation: 2783

java use thread to avoid GUI to deadlock

I got a project where I draw a complex form on a canvas, dependant on user input.

Now, calculating and drawing the form takes quite some time, so I put the calculating and drawing on separate threads:

First I calculate all the edges, then I draw them to a separate canvas, then I copy the content from the separate canvas to the screen-canvas.

calculation code

// 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);
    }

Then I draw it with default code (using GraphicsContext), that code works, so I won't copy it here.

Then I copy the content to the screen with this:

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

But here's my problem: no matter how I try, I always end up deadlocking the main thread: if I use join(), it will wait for the threads to end. So then I thought: I can use a timer that checks if the threads are done like this:

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);

And that's when I found out that timers are on a separate thread as well.

So my question is: how can I make sure that I wait to draw to the canvas until all the threads are done doing their stuff, without blocking/deadlocking the main thread?

Below are just examples of how the GUI looks like, so you have an idea. First example

2nd example

Upvotes: 1

Views: 108

Answers (2)

localghost
localghost

Reputation: 419

Based on your code snippet, you'll need to extend TimerTask providing your own implementation.

In your implementation pass a reference to the object responsible for accessing the running variable through it's constructor and then check for running through this object.

Upvotes: 1

Nadir
Nadir

Reputation: 1819

If you dont want to use join, you can use monitors to control the thread execution

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();
    }   
}

You would construct this object with the number of threads you will launch. When each thread is finished, it will call updateFinishedThread() . The main thread should call awaitThreads() after it launches the computation threads.

Upvotes: 1

Related Questions