views:

4761

answers:

11

I have a program that continually polls the database for change in value of some field. It runs in the background and currently uses a while(true) and a sleep() method to set the interval. I am wondering if this is a good practice? And, what could be a more efficient way to implement this? The program is meant to run at all times.

Consequently, the only way to stop the program is by issuing a kill on the process ID. The program could be in the middle of a JDBC call. How could I go about terminating it more gracefully? I understand that the best option would be to devise some kind of exit strategy by using a flag that will be periodically checked by the thread. But, I am unable to think of a way/condition of changing the value of this flag. Any ideas?

+8  A: 

This is really too big an issue to answer completely in this format. Do yourself a favour and go buy Java Concurrency in Practice. There is no better resource for concurrency on the Java 5+ platform out there. There are whole chapters devoted to this subject.

On the subject of killing your process during a JDBC call, that should be fine. I believe there are issues with interrupting a JDBC call (in that you can't?) but that's a different issue.

cletus
+1 If you find the Goetz book a little overwhelming (I did at first, there is a lot of information in it) I'd suggest reading Java Threads 3rd edition for an easer introduction with more code.
Bedwyr Humphreys
Is there a summary for this specific question? Like "use this class instead" ? Or something?
OscarRyz
+2  A: 

Set up a signal handler for SIGTERM that sets a flag telling your loop to exit its next time through.

chaos
"Set up a signal handler for SIGTERM" How do you do this?
OscarRyz
google 'java signal handler'.
chaos
+6  A: 

" I am wondering if this is a good practice?"

No. It's not good. Sometimes, it's all you've got, but it's not good.

"And, what could be a more efficient way to implement this?"

How do things get into the database in the first place?

The best change is to fix programs that insert/update the database to make requests which go to the database and to your program. A JMS topic is good for this kind of thing.

The next best change is to add a trigger to the database to enqueue each insert/update event into a queue. The queue could feed a JMS topic (or queue) for processing by your program.

The fall-back plan is your polling loop.

Your polling loop, however, should not trivially do work. It should drop a message into a queue for some other JDBC process to work on. A termination request is another message that can be dropped into the JMS queue. When your program gets the termination message, it absolutely must be finished with the prior JDBC request and can stop gracefully.

Before doing any of this, look at ESB solutions. Sun's JCAPS or TIBCO already have this. An open source ESB like Mulesource or Jitterbit may already have this functionality already built and tested.

S.Lott
A: 

If that's your application and you can modify it, you can:

  • Make it read a file
  • Read for the value of a flag.
  • When you want to kill it, you just modify the file and the application will exit gracefully.

Not need to work it that harder that that.

OscarRyz
Since it's a while loop with an interval of around 8 secs, the IO involved in reading the file will be quite a lot.
Epitaph
+3  A: 

Note that a Timer (or similar) would be better in that you could at least reuse it and let it do with all of the details of sleeping, scheduling, exception handling, etc...

There are many reasons your app could die. Don't focus on just the one.

If it's even theoretically possible for your JDBC work to leave things in a half-correct state, then you have a bug you should fix. All of your DB work should be in a transaction. It should go or not go.

Dustin
+1  A: 

Regarding the question "The program could be in the middle of a JDBC call. How could I go about terminating it more gracefully?" - see How can I abort a running jdbc transaction?

Note that using a poll with sleep() is rarely the correct solution - implemented improperly, it can end up hogging CPU resources (the JVM thread-scheduler ends up spending inordinate amount of time sleeping and waking up the thread).

noahz
+3  A: 

This is Java. Move your processing to a second thread. Now you can

  • Read from stdin in a loop. If someone types "QUIT", set the while flag to false and exit.
  • Create a AWT or Swing frame with a STOP button.
  • Pretend you are a Unix daemon and create a server socket. Wait for someone to open the socket and send "QUIT". (This has the added bonus that you can change the sleep to a select with timeout.)

There must be hundreds of variants on this.

jmucchiello
A: 

I‘ve created a Service class in my current company’s utility library for these kinds of problems:

public class Service implements Runnable {

    private boolean shouldStop = false;

    public synchronized stop() {
        shouldStop = true;
        notify();
    }

    private synchronized shouldStop() {
        return shouldStop;
    }

    public void run() {
        setUp();
        while (!shouldStop()) {
            doStuff();
            sleep(60 * 1000);
        }
    }

    private synchronized sleep(long delay) {
        try {
            wait(delay);
        } catch (InterruptedException ie1) {
            /* ignore. */
        }
    }

}

Of course this is far from complete but you should get the gist. This will enable you to simply call the stop() method when you want the program to stop and it will exit cleanly.

Bombe
Small comment - handle the InterruptedException - see http://www-128.ibm.com/developerworks/java/library/j-jtp05236.html
noahz
Thanks for the link! Great Article!
Andrei Vajna II
+4  A: 

As others have said, the fact that you have to poll is probably indicative of a deeper problem with the design of your system... but sometimes that's the way it goes, so...

If you'd like to handle "killing" the process a little more gracefully, you could install a shutdown hook which is called when you hit ctrl+C:

volatile boolean stop = false;

Runtime.getRuntime().addShutdownHook(new Thread("shutdown thread") {

        public void run()
        {
            stop = true;
        }
});

then periodically check the stop variable.

A more elegant solution is to wait on an event:

boolean stop = false;
final Object event = new Object();

Runtime.getRuntime().addShutdownHook(new Thread("shutdown thread") {

        public void run()
        {
            synchronized(event)
            {
               stop = true;
               event.notifyAll();
            }
        }
});

// ... and in your polling loop ...
synchronized(event)
{
    while(!stop)
    {
        // ... do JDBC access ...
        try
        {
            // wait 30 seconds, but break out as soon as the event is fired.
            event.wait(30000);
        }
        catch(InterruptedException e)
        {
            // Log a message and exit. Never ignore interrupted exception.
            break;
        }
    }
}

Or something like that.

Dave Ray
A: 

You could make the field a compound value that includes (conceptually) a process-ID and a timestamp. [Better yet, use two or more fields.] Start a thread in the process that owns access to the field, and have it loop, sleeping and updating the timestamp. Then a polling process that is waiting to own access to the field can observe that the timestamp has not updated in some time T (which is much greater than the time of the updating loop's sleep interval) and assume that the previously-owning process has died.

But this is still prone to failure.

In other languages, I always try to use flock() calls to synchronize on a file. Not sure what the Java equivalent is. Get real concurrency if you at all possibly can.

skiphoppy
A: 

I'm surprised nobody mentioned the interrupt mechanism implemented in Java. It's supposed to be a solution to the problem of stopping a thread. All other solutions have at least one flaw, that's why this mechanism is needed to be implemented in the Java concurrency library.

You can stop a thread by sending it an interrupt() message, but there are others ways that threads get interrupted. When this happens an InterruptedException is thrown. That's why you have to handle it when calling sleep() for example. That's where you can do cleanup and end gracefully, like closing the database connection.

Andrei Vajna II