Book Home Java Servlet Programming Search this book

3.5. Background Processing

Servlets can do more than simply persist between accesses. They can also execute between accesses. Any thread started by a servlet can continue executing even after the response has been sent. This ability proves most useful for long-running tasks whose incremental results should be made available to multiple clients. A background thread started in init() performs continuous work while request-handling threads display the current status with doGet(). It's a similar technique to that used in animation applets, where one thread changes the picture and another paints the display.

Example 3-6 shows a servlet that searches for prime numbers above one quadrillion. It starts with such a large number to make the calculation slow enough to adequately demonstrate caching effects--something we need for the next section. The algorithm it uses couldn't be simpler: it selects odd-numbered candidates and attempts to divide them by every odd integer between 3 and their square root. If none of the integers evenly divides the candidate, it is declared prime.

Example 3-6. On the hunt for primes

import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class PrimeSearcher extends HttpServlet implements Runnable {

  long lastprime = 0;                    // last prime found
  Date lastprimeModified = new Date();   // when it was found
  Thread searcher;                       // background search thread

  public void init(ServletConfig config) throws ServletException {
    super.init(config);                  // always!
    searcher = new Thread(this);
    searcher.setPriority(Thread.MIN_PRIORITY);  // be a good citizen
    searcher.start();
  }

  public void run() {
    //               QTTTBBBMMMTTTOOO
    long candidate = 1000000000000001L;  // one quadrillion and one

    // Begin loop searching for primes
    while (true) {                       // search forever
      if (isPrime(candidate)) {
        lastprime = candidate;           // new prime
        lastprimeModified = new Date();  // new "prime time"
      }
      candidate += 2;                    // evens aren't prime

      // Between candidates take a 0.2 second break.
      // Another way to be a good citizen with system resources.
      try {
        searcher.sleep(200);
      } 
      catch (InterruptedException ignored) { }
    }
  }

  private static boolean isPrime(long candidate) {
    // Try dividing the number by all odd numbers between 3 and its sqrt
    double sqrt = Math.sqrt(candidate);
    for (long i = 3; i <= sqrt; i += 2) {
      if (candidate % i == 0) return false;  // found a factor
    }

    // Wasn't evenly divisible, so it's prime
    return true;
  }

  public void doGet(HttpServletRequest req, HttpServletResponse res) 
                               throws ServletException, IOException {
    res.setContentType("text/plain");
    PrintWriter out = res.getWriter();
    if (lastprime == 0) {
      out.println("Still searching for first prime...");
    }
    else {
      out.println("The last prime discovered was " + lastprime);
      out.println(" at " + lastprimeModified);
    }
  }

  public void destroy() {
    searcher.stop();
  }
}

The searcher thread begins its search in the init() method. Its latest find is saved in lastprime, along with the time it was found in in lastprimeModified. Each time a client accesses the servlet, the doGet() method reports the largest prime found so far and the time it was found. The searcher runs independently of client accesses; even if no one accesses the servlet it continues to find primes silently. If several clients access the servlet at the same time, they all see the same current status.

Notice that the destroy() method stops the searcher thread.[6] This is very important! If a servlet does not stop its background threads, they continue to run until the virtual machine exits. Even when a servlet is reloaded (either explicitly or because its class file changed), its threads won't be stopped. Instead, it's likely that the new servlet will create extra copies of the background threads. And, at least with the Java Web Server, even explicitly restarting the web server service doesn't stop background threads because the Java Web Server virtual machine continues its execution.

[6] Stopping threads using the stop() method as shown here is deprecated in JDK 1.2 in favor of a safer flag-based system, where a thread must periodically examine a "flag" variable to determine when it should stop, at which point it can clean up and return from its run() method. See the JDK documentation for details. Example source code can be found in an article titled "Scott's Solutions: Programming with threads in Java 1.2", written by Scott Oaks for Java Report Online, found at http://www.sigs.com/jro/features/9711/oaks.html.



Library Navigation Links

Copyright © 2001 O'Reilly & Associates. All rights reserved.