The Artima Developer Community
Sponsored Link

Objects and Java Seminar by Bill Venners
Threads
Lecture Handout

Agenda


Multi-Threading in Java


Subclassing Thread


Implementing Runnable


Mutual Exclusion


The Thread-Safe Object

 1 // In file objectidioms/ex6/RGBColor.java
 2 // Instances of this class are NOT thread-safe.
 3
 4 public class RGBColor {
 5
 6     private int r;
 7     private int g;
 8     private int b;
 9
10     public RGBColor(int r, int g, int b) {
11
12         checkRGBVals(r, g, b);
13
14         this.r = r;
15         this.g = g;
16         this.b = b;
17     }
18
19     public void setColor(int r, int g, int b) {
20
21         checkRGBVals(r, g, b);
22
23         this.r = r;
24         this.g = g;
25         this.b = b;
26     }
27
28     /**
29     * returns color in an array of three ints: R, G, and B
30     */
31     public int[] getColor() {
32
33         int[] retVal = new int[3];
34         retVal[0] = r;
35         retVal[1] = g;
36         retVal[2] = b;
37
38         return retVal;
39     }
40
41     public void invert() {
42
43         r = 255 - r;
44         g = 255 - g;
45         b = 255 - b;
46     }
47
48     private static void checkRGBVals(int r, int g, int b) {
49
50         if (r < 0 || r > 255 || g < 0 || g > 255 ||
51             b < 0 || b > 255) {
52
53             throw new IllegalArgumentException();
54         }
55     }
56 }

Write/Write Conflicts

Thread Statement r g b Color
none object represents green 0 255 0  GREEN 
blue blue thread invokes setColor(0, 0, 255) 0 255 0  GREEN 
blue checkRGBVals(0, 0, 255); 0 255 0  GREEN 
blue this.r = 0; 0 255 0  GREEN 
blue this.g = 0; 0 255 0  GREEN 
blue blue gets preempted 0 0 0  BLACK 
red red thread invokes setColor(255, 0, 0) 0 0 0  BLACK 
red checkRGBVals(255, 0, 0); 0 0 0  BLACK 
red this.r = 255; 0 0 0  BLACK 
red this.g = 0; 255 0 0  RED 
red this.b = 0; 255 0 0  RED 
red red thread returns 255 0 0  RED 
blue later, blue thread continues 255 0 0  RED 
blue this.b = 255 255 0 0  RED 
blue blue thread returns 255 0 255  MAGENTA 
none object represents magenta 255 0 255  MAGENTA 

Read/Write Conflicts

Thread Statement r g b Color
none object represents green 0 255 0  GREEN 
blue blue thread invokes setColor(0, 0, 255) 0 255 0  GREEN 
blue checkRGBVals(0, 0, 255); 0 255 0  GREEN 
blue this.r = 0; 0 255 0  GREEN 
blue this.g = 0; 0 255 0  GREEN 
blue blue gets preempted 0 0 0  BLACK 
red red thread invokes getColor() 0 0 0  BLACK 
red int[] retVal = new int[3]; 0 0 0  BLACK 
red retVal[0] = 0; 0 0 0  BLACK 
red retVal[1] = 0; 0 0 0  BLACK 
red retVal[2] = 0; 0 0 0  BLACK 
red return retVal; 0 0 0  BLACK 
red red thread returns black 0 0 0  BLACK 
blue later, blue thread continues 0 0 0  BLACK 
blue this.b = 255 0 0 0  BLACK 
blue blue thread returns 0 0 255  BLUE 
none object represents blue 0 0 255  BLUE 

Thread-Safe RGBColor Object

 1 // In file objectidioms/ex7/RGBColor.java
 2 // Instances of this class are thread-safe.
 3
 4 public class RGBColor {
 5
 6     private int r;
 7     private int g;
 8     private int b;
 9
10     public RGBColor(int r, int g, int b) {
11
12         checkRGBVals(r, g, b);
13
14         this.r = r;
15         this.g = g;
16         this.b = b;
17     }
18
19     public void setColor(int r, int g, int b) {
20
21         checkRGBVals(r, g, b);
22
23         synchronized (this) {
24
25             this.r = r;
26             this.g = g;
27             this.b = b;
28         }
29     }
30
31     /**
32     * returns color in an array of three ints: R, G, and B
33     */
34     public int[] getColor() {
35
36         int[] retVal = new int[3];
37
38         synchronized (this) {
39
40             retVal[0] = r;
41             retVal[1] = g;
42             retVal[2] = b;
43         }
44
45         return retVal;
46     }
47
48     public synchronized void invert() {
49
50         r = 255 - r;
51         g = 255 - g;
52         b = 255 - b;
53     }
54
55     private static void checkRGBVals(int r, int g, int b) {
56
57         if (r < 0 || r > 255 || g < 0 || g > 255 ||
58             b < 0 || b > 255) {
59
60             throw new IllegalArgumentException();
61         }
62     }
63 }

Ready for Threads

Thread Statement r g b Color
none object represents green 0 255 0  GREEN 
blue blue thread invokes setColor(0, 0, 255) 0 255 0  GREEN 
blue checkRGBVals(0, 0, 255); 0 255 0  GREEN 
blue blue thread acquires lock 0 255 0  GREEN 
blue this.r = 0; 0 255 0  GREEN 
blue this.g = 0; 0 255 0  GREEN 
blue blue gets preempted 0 0 0  BLACK 
red red thread invokes setColor(255, 0, 0) 0 0 0  BLACK 
red checkRGBVals(255, 0, 0); 0 0 0  BLACK 
red red thread blocks because object locked 0 0 0  BLACK 
blue later, blue thread continues 0 0 0  BLACK 
blue this.b = 255 0 0 0  BLACK 
blue blue thread returns and releases lock 0 0 255  BLUE 
red later, red thread acquires lock and continues 0 0 255  BLUE 
red this.r = 255; 0 0 255  BLUE 
red this.g = 0; 255 0 255  MAGENTA 
red this.b = 0; 255 0 255  MAGENTA 
red red thread returns and releases lock 255 0 0  RED 
none object represents red 255 0 0  RED 

The Thread-Safe Object


Synchronized Class Methods


Thread Cooperation


The Java Monitor


Cooperation Example

 1 // In file threads/ex6/IntBuffer.java
 2 public class IntBuffer {
 3
 4     private final int buffSize;
 5     private int[] buff;
 6
 7     // Keeps track of next buff array location
 8     // to be filled. When nextBuffIndex ==
 9     // buffSize, the buffer is full. When
10     // nextBuffIndex == 0, the buffer is
11     // empty.
12     private int nextBuffIndex;
13
14     IntBuffer(int buffSize) {
15
16         this.buffSize = buffSize;
17         buff = new int[buffSize];
18     }
19
20     public synchronized void add(int val) {
21
22         while (nextBuffIndex == buffSize) {
23
24             try {
25                 wait();
26             }
27             catch (InterruptedException e) {
28             }
29         }
30
31         buff[nextBuffIndex] = val;
32         ++nextBuffIndex;
33
34         notifyAll();
35     }
36
37     public synchronized int removeNext() {
38
39         while (nextBuffIndex == 0) {
40
41             try {
42                 wait();
43             }
44             catch (InterruptedException e) {
45             }
46         }
47
48         // This buffer is FIFO, so remove the
49         // first int added and shift the rest
50         // over.
51         int val = buff[0];
52
53         --nextBuffIndex;
54         for (int i = 0; i < nextBuffIndex; ++i) {
55
56             buff[i] = buff[i + 1];
57         }
58
59         notifyAll();
60         return val;
61     }
62 }

 1 // In file threads/ex6/PrimeNumberGenerator.java
 2 public class PrimeNumberGenerator implements Runnable {
 3
 4     private final IntBuffer buff;
 5
 6     public PrimeNumberGenerator(IntBuffer buff) {
 7
 8         this.buff = buff;
 9     }
10
11     public void run() {
12
13         int primeNum = 1;
14         int numToCheck = 2;
15
16         buff.add(primeNum);
17
18         for (;;) {
19
20             boolean foundPrime = true;
21
22             for (int divisor = numToCheck / 2; divisor > 1;
23                 --divisor) {
24
25                 if (numToCheck % divisor == 0) {
26                     foundPrime = false;
27                     break;
28                 }
29             }
30
31             if (foundPrime) {
32                 primeNum = numToCheck;
33                 buff.add(primeNum);
34             }
35
36             ++numToCheck;
37         }
38     }
39 }

 1 // In source packet in file threads/ex6/IntPrinter.java
 2 public class IntPrinter implements Runnable {
 3
 4     private final IntBuffer buff;
 5
 6     public IntPrinter(IntBuffer buff) {
 7
 8         this.buff = buff;
 9     }
10
11     public void run() {
12
13         for (;;) {
14
15             int val = buff.removeNext();
16             System.out.println(val);
17         }
18     }
19 }

 1 // In file threads/ex6/Example6.java
 2 public class Example6 {
 3
 4     public static void main(String[] args) {
 5
 6         IntBuffer buff = new IntBuffer(3);
 7
 8         PrimeNumberGenerator png = new PrimeNumberGenerator(buff);
 9         IntPrinter ip = new IntPrinter(buff);
10
11         Thread producer = new Thread(png);
12         Thread consumer = new Thread(ip);
13
14         producer.start();
15         consumer.start();
16     }
17 }

Thread Blocking


Program Liveness


Thread Scheduling


Exercise: The Dreaded, Threaded Fibonacci Generator

Create a Java application named Problem1 that generates the Fibonacci sequence. The first two numbers of the Fibonacci sequence are 1 and 1. Each subsequent number is calculated by summing the previous two numbers, as in: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, and so on.

Input to the Application

The Problem1 application will write the Fibonacci sequence to the standard output. The Problem1 application, which requires no command line arguments, should print out the first 92 Fibonacci numbers The output will look like:

1
1
2
3
5
8
13
<...>
The maximum of 92 arises because the 93rd Fibonacci number is too big to express in a Java long. The biggest Fibonacci number that will fit in Java's long (a 64 bit signed integer) is 7540113804746346429L, which is the 92nd Fibonacci number.

Structure of the Application

The application will be made up of four classes, named:

FibonacciGenerator.java
LongBuffer.java
LongBufferToOutputThread.java
Problem1.java

The application will contain three threads, the main thread and two extra threads that the main thread will start. The two extra threads are defined by FibonacciGenerator, which implements Runnable, and LongBufferToOutputThread, which directly subclasses class Thread.

The main() method of the Problem1 application will create and start these two threads and connect the output of the FibonacciGenerator thread to the input of the LongBufferToOutputThread. The Fibonacci numbers will be generated by the FibonacciGenerator thread, which writes one long value at time into a LongBuffer. The LongBufferToOutputThread will then read long's from the LongBuffer and write them to the standard output.

Classes of the Application

Class Problem1

The main() method should:

Class LongBuffer

You can base this class on the IntBuffer class from the lecture slides, which is in the Threads/examples/ex6 directory of the sample code:

// In source packet in file threads/ex6/IntBuffer.java
public class IntBuffer {
    private final int buffSize;
    private int[] buff;
    // Keeps track of next buff array location
    // to be filled. When nextBuffIndex ==
    // buffSize, the buffer is full. When
    // nextBuffIndex == 0, the buffer is
    // empty.
    private int nextBuffIndex;
    IntBuffer(int buffSize) {
        this.buffSize = buffSize;
        buff = new int[buffSize];
    }
    public synchronized void add(int val) {
        while (nextBuffIndex == buffSize) {
            try {
                wait();
            }
            catch (InterruptedException e) {
            }
        }
        buff[nextBuffIndex] = val;
        ++nextBuffIndex;
        notifyAll();
    }
    public synchronized int removeNext() {
        while (nextBuffIndex == 0) {
            try {
                wait();
            }
            catch (InterruptedException e) {
            }
        }
        // This buffer is FIFO, so remove the
        // first int added and shift the rest
        // over.
        int val = buff[0];
        --nextBuffIndex;
        for (int i = 0; i < nextBuffIndex; ++i) {
            buff[i] = buff[i + 1];
        }
        notifyAll();
        return val;
    }
}

Basically, LongBuffer has to do a similar thing to what IntBuffer does, but for longs instead of ints. It needs an add() method and a long removeNext() method, and it must assume different threads will be calling these methods. Thus, the add() and removeNext() methods must be synchronized and use wait() and notifyAll().

Class FibonacciGenerator

This class extends Object and implements Runnable. It has one constructor, which takes one argument: a LongBuffer reference.

It's run() method simply produces the Fibonacci sequence one long at a time and writes each one to the LongBuffer as it is produced. To indicate that it is finished producing numbers, the FibonacciGenerator class declares a public static final int END_OF_DATA field that is initialized to -1. When the FibonacciGenerator's run() method is done generating the first 92 Fibonacci numbers, it writes an END_OF_DATA to the LongBuffer. After that, this thread is finished and the run() method simply returns.

Class LongBufferToOutputThread

This class extends Thread. It has one constructor, which takes one argument: a LongBuffer.

It's run() method simply reads one long at a time from the LongBuffer and writes it as a String to the standard output, placing a return ('\n') after each number it prints. It keeps doing this until it reads an FibonacciGenerator.END_OF_DATA from the LongBuffer. When it finds END_OF_DATA, the run() method returns, and this thread expires.

Odds and Ends

How the app knows to terminate: A Java application terminates when all non-daemon threads expire. In this application, there are three non-daemon threads. The main thread sets up and starts the other two threads, then returns. One thread down. The FibonacciGenerator thread generates the numbers, stores them into the LongBuffer, then writes an END_OF_DATA into the LongBuffer, and returns. By returning from run(), the FibonacciGenerator thread expires. Two threads down. The LongBufferToOutputThread reads from the LongBuffer and writes to the standard output until it finds an END_OF_DATA in the LongBuffer. It then returns. By returning from run(), the LongBufferToOutputThread thread expires. Because this is the third and only remaining non-daemon thread, the entire application terminates.


Sponsored Links

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use