Four Kinds of Threads Programming

Coordination between different threads is known as synchronization. Programs that use threads can be divided into the following four levels of difficulty, depending on the kind of synchronization needed between the different threads.

  1. Unrelated threads

  2. Related but unsynchronized threads

  3. Mutually exclusive threads

  4. Communicating mutually exclusive threads

We will deal with the first two here and the second two in the next chapter. Figure 13-3 is the key for all illustrations dealing with this topic.

Figure 13-3. Key to threads diagrams

image

Unrelated threads

The simplest threads program involves threads of control that do different things and don't interact with each other.

A good example of unrelated threads is the answer to the programming challenge set a few pages back.

The code is as follows:

public class drinks {
   public static void main(String[] a) {
      Coffee t1 = new Coffee();
      t1.start();
      new Tea().start();  // an anonymous thread
   }
}

class Coffee extends Thread {
   public void run() {
         while(true) {
             System.out.println("I like coffee");
             yield();  // did you forget this?
         }
   }
}

class Tea extends Thread {
   public void run() {
         while(true) {
             System.out.println("I like tea");
             yield();
         }
   }
}

When you run this program, you will see output similar to the following:

I like coffee

I like tea

I like coffee

I like tea

I like coffee

I like tea

It is repeated over and over again until you press control-C or otherwise interrupt program execution. This type of threads programming is easy to get working, and it corresponds to Figure 13-4.

Figure 13-4. Unrelated threads

image

Related but unsynchronized threads

This level of complexity uses threaded code to partition a problem, solving it by having multiple threads work on different pieces of the same data structure. The threads don't interact with each other. Here, threads of control do work that is sent to them, but they don't work on shared data, so they don't need to access it in a synchronized way.

An example of this would be spawning a new thread for each socket connection that comes in. A thread that just does “work to order” like that is a good example of a demon thread—its only purpose is to serve a higher master. See Figure 13-5 for a graphical representation of how this kind of thread interacts with objects.

Figure 13-5. Related but unsynchronized threads.

image

A less common but still interesting example of related but unsynchronized threads involves partitioning a data set and instantiating multiple copies of the same thread to work on different pieces of the same problem. Be careful not to duplicate work, or even worse, to let two different threads operate on the same data at once.

The following is an example program that tests whether a given number is a prime number. That involves a lot of calculations that don't affect each other so it's a good candidate for parcelling the work out among a number of threads. Tell each thread the range of numbers it is to test-divide into the possible prime. Then let them all loose in parallel.

The driver code is:

// demonstrates the use of threads to test a number for primality

public class testPrime {

   public static void main(String s[]) {
        long possPrime = Long.parseLong(s[0]);
        int centuries = (int)(possPrime/100) +1;

        for(int i=0;i<centuries;i++) {
             new testRange(i*100, possPrime).start();
        }

   }
}

This main program gets its argument, which is the value to test for primarily, and then calculates how many 100s there are in the number. A new thread is created to test for factors in every range of 100. So if the number is 2048, there are twenty 100s. Twenty one threads are created. The first checks whether any of the numbers 2 to 99 divide into 2048. The second checks the range 100 to 199. The third checks 200 to 299, and so on, with the 21st thread checking the numbers 2000 to 2100.

The line “new testRange(i*100, possPrime).start();” instantiates an object of class testRange, using the constructor that takes two arguments. That object belongs to a subclass of Thread, so the .start() jammed on the end starts it running. This is the Java idiom of invoking a method on the object returned by a constructor or method invocation. The listing of class testRange is as follows:

class testRange extends Thread {

   static long possPrime;
   long from, to;

   // constructor
   //   record the number we are to test, and
   //   the range of factors we are to try.
   testRange(int argFrom,long argpossPrime) {
       possPrime=argpossPrime;
       if (argFrom==0) from=2; else from=argFrom;
       to=argFrom+99;
   }

   public void run() {
      for (long i=from; i<=to && i<possPrime; i++) {
         if (possPrime % i == 0) {  // i divides possPrime exactly
             System.out.println(
                    "factor "+i+" found by thread "+getName());
             break;  // get out of for loop
         }
         yield();
      }
   }

}

The constructor saves a copy of two things: The number we are to test for primarily, and the start of the range of potential factors for this thread instance to test. The end of the range is the start plus 99.

All the run() method does is count through this range, trying each divisor. If one divides the number exactly, then print it out and stop this thread. We have the answer that the number is not prime. There are many possible improvements to the algorithm (for instance, we need only test for factors up to the square root of the possible prime). These improvements have been omitted so as not to clutter up the code example.

A sample run of this program might look like the following:

% java testPrime 2048
 factor 2 found by thread Thread-4
 factor 512 found by thread Thread-9
 factor 1024 found by thread Thread-14
 factor 128 found by thread Thread-5
 factor 256 found by thread Thread-6

So, 2048 is not a prime number and five of the 21 threads found factors. The default name for the first thread you create is Thread-4 (not Thread-1) because there are already several threads running in your program, including the garbage collector and your main program.

The following code shows the non-private members of the class java.lang.Thread. You can look at the full source at srcjavalangThread.java.

public class java.lang.Thread implements java.lang.Runnable {
     java.lang.InheritableThreadLocal$Entry values;
     public static final int MIN_PRIORITY;
     public static final int NORM_PRIORITY;
     public static final int MAX_PRIORITY;
// constructors
     public java.lang.Thread();
     public java.lang.Thread(java.lang.Runnable);
     public java.lang.Thread(java.lang.Runnable,java.lang.String);
     public java.lang.Thread(java.lang.String);
     public java.lang.Thread(ThreadGroup,java.lang.Runnable);
     public Thread(ThreadGroup, Runnable, String);
     public java.lang.Thread(ThreadGroup,String);

     public static int activeCount();
     public final void checkAccess();
     public static native java.lang.Thread currentThread();
     public void destroy();
     public static void dumpStack();
     public static int enumerate(java.lang.Thread[]);
     public java.lang.ClassLoader getContextClassLoader();
     public final java.lang.String getName();
     public final int getPriority();
     public final java.lang.ThreadGroup getThreadGroup();
     public void interrupt();
     public static boolean interrupted();
     public final native boolean isAlive();
     public final boolean isDaemon();
     public boolean isInterrupted();
     public final void join() throws java.lang.InterruptedException;
     public final synchronized void join(long) throws
                                             InterruptedException;
    public final synchronized void join(long, int) throws
                                   InterruptedException;
     public void run();
     public void setContextClassLoader(java.lang.ClassLoader);
     public final void setDaemon(boolean);
     public final void setName(java.lang.String);
     public final void setPriority(int);
     public static native void sleep(long) throws
                                        java.lang.InterruptedException;
public static void sleep(long, int) throws java.lang.InterruptedException;
     public java.lang.String toString();
     public static native void yield();
     public native synchronized void start();

// deprecated methods: do not use.
     public native int countStackFrames();             // deprecated
     public final void stop();                         // deprecated
     public final synchronized void stop(java.lang.Throwable); //deprecated
     public final void suspend();                      // deprecated
     public final void resume();                       // deprecated
}

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.39.144