6

I have an object that is being initialized in a separate thread. Initialization can take several seconds while a local DB is being populated.

SpecialAnalysis currentAnalysis = new SpecialAnalysis(params_here);

I'm trying to implement a "cancel" button, that sets the object's isCancelled boolean to true. What is the proper Java way to implement this?

while (currentAnalysis == null) {
}
currentAnalysis.cancel();

This method freezes the program as it appears to have entered a computationally inefficient loop. Is this a case where I could use Object.wait()?

My current bad/semi-successful solution is:

while (currentAnalysis == null) {
    Thread.sleep(500);
}
currentAnalysis.cancel();

Thanks!

willjcroz
  • 2,106
  • 1
  • 25
  • 33
Megatron
  • 15,909
  • 12
  • 89
  • 97

2 Answers2

8

Firstly, yes Object.wait() and Object.notify() / Object.notifyAll() are what you need. Whether or not you use them directly is a different matter. Due to the ease of making mistakes programming directly with wait/notify it is generally recommended to use the concurrency tools added in Java 1.5 (see second approach below).


The traditional wait/notify approach:

Initialisation:

    synchronized (lockObject) {
        SpecialAnalysis currentAnalysis = new SpecialAnalysis(params_here);
        lockObject.notifyAll();
    }

In the 'cancel' thread:

    synchronized (lockObject) {
        while (currentAnalysis == null) {
            try { lockObject.wait(); }
            catch Exception(e) { } // FIXME: ignores exception
        }
    }
    currentAnalysis.cancel();

Of course these could be synchronized methods instead of blocks. Your choice of lockObject will depend on how many 'cancel' threads you need etc. In theory it could be anything, i.e. Object lockObject = new Object(); as long as you are careful the correct threads have access to it.

Note that it is important to put the call to wait() in a while loop here due to the possibility of spurious wakeups coming from the underlying OS.


A simpler approach would be to use a CountDownLatch, sparing you from the nuts and bolts of wait()&notify():

(I'm making a couple of assumptions here in order to suggest a possibly cleaner approach).

    class AnalysisInitialiser extends Thread {

        private CountDownLatch cancelLatch = new CountDownLatch(1);
        private SpecialAnalysis analysis = null;

        @Override
        public void run() {
            analysis = new SpecialAnalysis(params);
            cancelLatch.countDown();
        }

        public SpecialAnalysis getAnalysis() {
            cancelLatch.await();
            return analysis;
        }
    }

Then in the thread that needs to send the cancel signal: (obviously you need to get hold of the AnalysisInitialiser object in some way)

    analysisInit.getAnalysis.cancel();

No concurrency primitive boilerplate, yay!

willjcroz
  • 2,106
  • 1
  • 25
  • 33
  • Thanks for the great solution. It seems a Semaphore is more appropriate than a CountDownLatch in this situation: http://stackoverflow.com/a/184800/2776559 – Megatron Jun 02 '14 at 13:10
  • 1
    I am going to try your first approach (using a monitor, if that's the correct terminology?). Great resource here as well: http://www.csc.villanova.edu/~mdamian/threads/javamonitors.html Thanks! – Megatron Jun 02 '14 at 13:26
  • @Megatron thanks, by all means use the monitor based approach, it is certainly useful for education. Just be careful, everytime you add `synchronized` and `wait/notify` to your code you are increasing the likelihood of liveness bugs. Our brains are not great at reasoning about complex concurrent systems! – willjcroz Jun 02 '14 at 14:31
  • @Megatron BTW the SO answer you link does not conflict with my suggestion above: this situation is a special case of 'start a series of threads and then wait until all of them are complete' in which we only start one extra thread. Semaphores are designed to restrict the number of threads executing within 'critical sections' of code. – willjcroz Jun 02 '14 at 14:37
  • 1
    Alright, I tried a few methods, and the CountDownLatch worked AMAZINGLY! Thank you so much for this solution. Highly recommend it! Simpler for me to encode than monitors or semaphores – Megatron Jun 02 '14 at 16:08
  • 1
    Great to hear :-) I recommend this book, which covers all the monitor-based `wait/notify` stuff as well as the `java.util.concurrent` tools: [Java Concurrency in Practice](http://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601) (only thing missing is the new `fork/join` and stream based APIs in more recent Java SE versions). – willjcroz Jun 02 '14 at 16:45
-1

i like this question so voted up..

you can do like below

    do {
    if(currentAnalysis != null){
    currentAnalysis.cancel();
    }
   }
     while (currentAnalysis == null)

here your do keeps checking the value of currentAnalysis and once its not null then it performs cancel else keeps looping and checking currentAnalysis value.

this is one better approach i am finding right now

Karibasappa G C
  • 2,686
  • 1
  • 18
  • 27