Skip to main content
Engineering LibreTexts

14.2: From the Java Library- java.lang.Thread

  • Page ID
    15148
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)\(\newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    The java.lang.Thread class contains the public methods shown in Figure 14.4 (the figure contains only a partial list). Note that Thread implements the Runnable interface, which consists simply of the run() method. As we will now see, another way to create a thread is to instantiate a Thread object and pass it a Runnable object that will become its body. This approach allows you to turn an existing class into a separate thread.

    A Runnable object is any object that implements the Runnable interface—that is, any object that implements the run() method (Fig. 14.5). The following example provides an alternative way to implement the NumberThread program:

    public class NumberPrinter implements Runnable {
        int num;
    
        public NumberPrinter(int n) {
            num = n;
        }
    
        public void run() {
            for (int k=0; k < 10; k++)
                System.out.print(num);
        } // run()
    } // NumberPrinter

    Given this definition, we would then pass instances of this class to the individual threads as we create them:

    public class Numbers {
      public static void main(String args[]) {
    
        Thread number1, number2, number3, number4, number5;
         // Create and start each thread
        number1 = new Thread(new NumberPrinter(1)); number1.start(); 
        number2 = new Thread(new NumberPrinter(2)); number2.start();
        number3 = new Thread(new NumberPrinter(3)); number3.start();
        number4 = new Thread(new NumberPrinter(4)); number4.start();
        number5 = new Thread(new NumberPrinter(5)); number5.start();
      } // main()
    } // Numbers

    The NumberPrinter class implements Runnable by defining exactly the same run() that was used previously in the NumberThread class. We then pass instances of NumberPrinter when we create the individual threads. Doing things this way gives exactly the same output as earlier. This example serves to illustrate another way of creating a multithreaded program:

    Implement the Runnable interface for an existing class by implementing the void run() method, which contains the statements to be executed by that thread.

    Create several Thread instances by first creating instances of the Runnable class and passing each instance as an argument to the Thread() constructor.

    For each thread instance, start it by invoking the start() method on it.

    Use the Runnable interface to convert the following class into a thread. You want the thread to print all the odd numbers up to its bound:

    public class PrintOdds {
        private int bound;
        public PrintOdds(int b) {
            bound = b;
        }
    
        public void print() {
            if (int k = 1; k < bound; k+=2)
                System.out.println(k);
        }
    } // PrintOdds

    Thread Control

    The various methods in the Thread class (Fig. 14.4) can be used to exert some control over a thread’s execution. The start() and stop() methods play the obvious roles of starting and stopping a thread. These methods will sometimes be called automatically. For example, an applet is treated as a thread by the browser, or appletviewer, which is responsible for starting and stopping it.

    As we saw in the NumberThread example, the run() method encapsulates the thread’s basic algorithm. It is usually not called directly. Instead, it is called by the thread’s start() method, which handles any system-dependent initialization tasks before calling run().

    Thread Priority

    The setPriority(int) method lets you set a thread’s priority to an integer value between Thread.MIN_PRIORITY and Thread.MAX_PRIORITY, the bounds defined as constants in the Thread class. Using setPriority() gives you some control over a thread’s execution. In general, higher-priority threads get to run before, and longer than, lower-priority threads.

    To see how setPriority() works, suppose we change NumberThread’s constructor to the following:

    public NumberThread(int n) {
        num = n;
        setPriority(n);
    }

    In this case, each thread sets its priority to its ID number. So, thread five will have priority five, a higher priority than all the other threads. Suppose we now run 2 million iterations of each of these threads. Because 2 million iterations will take a long time if we print the thread’s ID on each iteration, let’s modify the run() method, so that the ID is printed every 1 million iterations:

    for (int k = 0; k < 10; k++)
        if (k % 1000000 == 0)
            System.out.print(num);

    Given this modification, we get the following output when we run :

    5544332211

    It appears from this output that the threads ran to completion in priority order. Thus, thread five completed 2 million iterations before thread four started to run, and so on. This shows that, on my system at least, the Java Virtual Machine (JVM) supports priority scheduling.

    Forcing Threads to Sleep

    The Thread.sleep() and Thread.yield() methods also provide some control over a thread’s behavior. When executed by a thread, the yield() method causes the thread to yield the CPU, allowing the thread scheduler to choose another thread. The sleep() method causes the thread to yield and not to be scheduled until a certain amount of real time has passed.

    The sleep() method can halt a running thread for a given number of milliseconds, allowing other waiting threads to run. The sleep() method throws an InterruptedException, which is a checked exception. This means that the sleep() call must be embedded within a try/catch block or the method it’s in must throw an InterruptedException. Try/catch blocks were covered in Chapter 10.

    try {
        sleep(100);
    } catch (InterruptedException e) {
        System.out.println(e.getMessage());
    }

    For example, consider the following version of the NumberPrinter.run():

    public void run() {
        for (int k=0; k < 10; k++) {
            try {
                Thread.sleep((long)(Math.random() * 1000));
            } catch (InterruptedException e) {
                System.out.println(e.getMessage());
            }
            System.out.print(num);
        } // for
    } // run()

    In this example, each thread is forced to sleep for a random number of milliseconds between 0 and 1,000. When a thread sleeps, it gives up the CPU, which allows one of the other waiting threads to run. As you would expect, the output we get from this example will reflect the randomness in the amount of time that each thread sleeps:

    14522314532143154232152423541243235415523113435451

    As we will see, the sleep() method provides a rudimentary form of thread synchronization, in which one thread yields control to another.

    What happens if you run five NumberThreads of equal priority through 2 million iterations each? Run this experiment and note the output. Don’t print after every iteration! What sort of scheduling algorithm (round-robin, priority scheduling, or something else) was used to schedule threads of equal priority on your system?

    Try the following experiment and note the output. Let each thread sleep for 50 milliseconds (rather than a random number of milliseconds). How does this affect the scheduling of the threads? To make things easier to see, print each thread’s ID after every 100,000 iterations.

    The purpose of the Java garbage collector is to recapture memory that was used by objects that are no longer being used by your program. Should its thread have higher or lower priority than your program?

    The Asynchronous Nature of Threaded Programs

    Threads are asynchronous. This means that the order of execution and the timing of a set of threads are unpredictable, at least from the programmer’s point of view. Threads are executed under the control of the scheduling algorithm used by the operating system and the Java Virtual Machine. In general, unless threads are explicitly synchronized, it is impossible for the programmer to predict when and for how long an individual thread will run. In some systems, under some circumstances, a thread might run to completion before any other thread can run. In other systems, or under different circumstances, a thread might run for a short time and then be suspended while another thread runs. Of course, when a thread is preempted by the system, its state is saved so that its execution can be resumed without losing any information.

    One implication of a thread’s asynchronicity is that it is not generally possible to determine where in its source code an individual thread might be preempted. You can’t even assume that a thread will be able to complete a simple Java arithmetic operation once it has started it. For example, suppose a thread had to execute the following operation:

    int N = 5 + 3;

    This operation computes the sum of 5 and 3 and assigns the result to N. It would be tempting to think that once the thread started this operation, it would be able to complete it, but that is not necessarily so. You have to remember that Java code is compiled into a rudimentary bytecode, which is translated still further into the computer’s machine language. In machine language, this operation would break down into something like the following three steps:

    Fetch 5 from memory and store it in register A.
    Add 3 to register A.
    Assign the value in register A to N.

    Although none of the individual machine instructions can be preempted, the thread could be interrupted between any two machine instructions. The point here is that not even a single Java language instruction can be assumed to be indivisible or unpreemptible. Therefore, it is impossible to make any assumptions about when a particular thread will run and when it will give up the CPU. This suggests the following important principle of multithreaded programs:

    As we will see, this principle plays a large role in the design of multithreaded programs.


    This page titled 14.2: From the Java Library- java.lang.Thread is shared under a CC BY 4.0 license and was authored, remixed, and/or curated by Ralph Morelli & Ralph Wade via source content that was edited to the style and standards of the LibreTexts platform; a detailed edit history is available upon request.

    • Was this article helpful?