SDSU CS 596 Java Programming
Fall Semester, 1998
Threads part 3
To Lecture Notes Index
© 1998, All Rights Reserved, SDSU & Roger Whitney
San Diego State University -- This page last updated 04-Jan-99

Contents of Doc 21, Threads part 3


References


Concurrent Programming in Java: Design Principles and Patterns , Doug Lea, Addison-Wesley, 1997

The Java Programming Language , 2 nd Ed. Arnold & Gosling, Addison-Wesley, 1998

The Java Language Specification , Gosling, Joy, Steele, Addison-Wesley, 1996, Chapter 17 Threads and Locks.

Java's Atomic Assignment, Art Jolin, Java Report , August 1998, pp 27-36.

Java 1.2b4 on-line documentation


Listen Here!S-nov4 6mins, S-nov4 3mins Doc 21, Threads part 3 Slide # 2

Safety - Mutual Access


With multiprocessing we need to address mutual access by different threads. When two or more threads simultaneously access the same data there may be problems.

Some types of access are safe. If a method accesses just local data, then multiple threads can safely call the method on the same object. Assignment statements of all types, except long and double, are atomic. That is a thread can not be interrupted by another thread while performing an atomic operation.

class  AccessExample {
   int[]  data;
   int safeInt;
   public String toString() {
      return "array length " + data.length + " array values " + data[0];
   }
   
   public  void safeCode( int size,  int  startValue){
      int[]  verySafe  =  new  int[ size ]; 
      for  ( int  index  = 0; index < size;  index++ )
         verySafe[ index ]  =  (int ) Math.sin( index * startValue );
   }
   public void setInt( int aValue ) {
      safeInt = aValue;
   }
   public void dangerousCode( int size,  int  startValue) {
      data  =  new  int[ size ];
      for  ( int  index  = 0; index < size;  index++ )
         data[ index ]  =  (int ) Math.sin( index * startValue );
   }
}

Listen Here!S-nov4 2mins, S-nov4 7mins Doc 21, Threads part 3 Slide # 3
Mutual Access Problem

class Trouble extends Thread {
   int size;
   int startValue;
   AccessExample data;
   
   public Trouble( int aSize, int aStartValue, AccessExample myData ) {
      size = aSize;
      startValue = aStartValue;
      data = myData;
   }
   
   public void run()    {
      for (int k = 0; k < 10; k++ ) {
         data.setInt( size);
         data.safeCode( size, startValue );
         data.dangerousCode( size, startValue);
      }
   }
}
public class Test  {
   public static void main( String args[] ) throws Exception {
   AccessExample someData = new AccessExample();
   Trouble one = new Trouble( 500000, 0, someData );
   Trouble two = new Trouble( 3, 22, someData );
   one.start();
   two.start();
   two.join();
   one.join();
   System.out.println( someData );
   }
}
Output
rohan 31-> j2 -native Test
java.lang.ArrayIndexOutOfBoundsException: 3
        at AccessExample.dangerousCode(Compiled Code)
        at Trouble.run(Compiled Code)
array length 3 array values 0

Listen Here!S-nov4 7mins Doc 21, Threads part 3 Slide # 4

Synchronize


Synchronize is Java's mechanism to insure that only one thread at a time will access a piece of code. We can synchronize methods and block's of code (synchronize statements).

Synchronized Instance Methods

When a thread executes a synchronized instance method on an object, that object is locked. The object is locked until the method ends. No other thread can execute any synchronized instance method on that object until the lock is released. The thread that has the lock can execute multiple synchronized methods on the same object. The synchronization is on a per object bases. If you have two objects, then different threads can simultaneously execute synchronized methods on different objects. Unsynchronized methods can be executed on a locked object by any thread at any time. The JVM insures that only one thread can obtain a lock on an object at a time.
class SynchronizeExample {
   int[]  data;
   public String toString() {
      return "array length " + data.length + " array values " + data[0];
   }
   
   public synchronized void initialize( int size,  int  startValue){
      data  =  new  int[ size ];
      for  ( int  index  = 0; index < size;  index++ )
         data[ index ]  =  (int ) Math.sin( index * startValue );
   }
   public  void unSafeSetValue( int  newValue) {
      for  ( int  index  = 0; index < data.length;  index++ )
         data[ index ]  =  (int ) Math.sin( index * newValue );
   }
   public  synchronized void safeSetValue( int  newValue) {
      for  ( int  index  = 0; index < data.length;  index++ )
         data[ index ]  =  (int ) Math.sin( index * newValue );
   }
}

Listen Here!S-nov4 2mins Doc 21, Threads part 3 Slide # 5

Synchronized Static Methods


A synchronized static method creates a lock on the class, not the object. When one thread has a lock on the class, no other thread can execute any synchronized static method of that class. Other threads can execute synchronized instance methods on objects of that class.

class SynchronizeStaticExample {
   int[]  data;
   static int[] classData 
   public synchronized void initialize( int size,  int  startValue){
      data  =  new  int[ size ];
      for  ( int  index  = 0; index < size;  index++ )
         data[ index ]  =  (int ) Math.sin( index * startValue );
   }
   public synchronized void initializeStatic( int size,  int  startValue){
      classData  =  new  int[ size ];
      for  ( int  index  = 0; index < size;  index++ )
         classData[ index ]  =  (int ) Math.sin( index * startValue );
   }
}


Listen Here!S-nov4 4mins Doc 21, Threads part 3 Slide # 6

Synchronized Statements


A block of code can be synchronized. The basic syntax is:

synchronized ( expr ) {
   statements
}
The expr must evaluate to an object. This will lock the object. The lock is released when the thread finishes the block. Until the lock is released, no other thread can enter any method or synchronized block that is locked by the given object.

A synchronized method is syntactic sugar for a synchronized block.

   class LockTest {
      public synchronized void enter() {
         System.out.println( "In enter");
      }
   }
Is the same as:

   class LockTest {
      public void enter() {
         synchronized ( this ) {
            System.out.println( "In enter");
         }
      }
   }

Listen Here!S-nov4 6mins Doc 21, Threads part 3 Slide # 7
Lock for Block and Method
This example shows that a lock on an object also locks all access to the object via synchronized methods.
public class Test  {
   public static void main( String args[] ) throws Exception {
      LockTest aLock = new LockTest();
      TryLock tester = new TryLock( aLock );
      tester.start();
      
      synchronized ( aLock ) {
         System.out.println( "In Block");
         Thread.currentThread().sleep( 5000);
         System.out.println( "End Block");
      }
   }
}
class TryLock extends Thread {
   private LockTest myLock;
   
   public TryLock( LockTest aLock ) {
      myLock = aLock;
   }
   public void run()   {
      System.out.println( "Start run");
      myLock.enter();
      System.out.println( "End run");
   }
}
class LockTest {
   public synchronized void enter() {
      System.out.println( "In enter");
   }
}
Output
In Block
Start run
End Block
In enter
End run

Doc 21, Threads part 3 Slide # 8
Deadlock
The following code creates a deadlock

class  Friendly  extends Thread {
   private  Friendly  aFriend;
   public  Friendly(  String  name ) {   super( name ); }
   public void setFriend( Friendly myFriend )
      { aFriend = myFriend;}
      
   public synchronized void hug() {
      try {
         System.out.println(  "I " + getName() + " am hugged ");
         sleep( 5 );
         aFriend.rehug();
      } catch ( InterruptedException notInThisExample ){}
   }
   public synchronized void rehug() {
      System.out.println( "I " + getName() + " am rehugged ");
   }
      
   public  void  run()   {aFriend.hug(); }
}
public class Test {
   public static void main( String args[] )  {
      Friendly  fred  =  new Friendly("Fred");
      Friendly  sam = new Friendly( "Sam");
      fred.setFriend( sam );
      sam.setFriend( fred );
      fred.start();
      sam.start();
      System.out.println( "End" );
   }
}
Output
End
I Fred am hugged
I Sam am hugged

Doc 21, Threads part 3 Slide # 9
Deadlock Avoided
Here we show how to avoid the deadlock of the previous slide.

class  Friendly  extends Thread   {
   private  Friendly  aFriend;
   private Object lock;
   public  Friendly(  String  name, Object lock )   {
      super( name );
      this.lock = lock;
   }
   public void setFriend( Friendly myFriend )   {
      aFriend = myFriend;
   }
      
   public synchronized void hug()    {
      try {
         System.out.println(  "I " + getName() + " am hugged ");
         sleep( 5 );
         aFriend.rehug();
      }
      catch ( InterruptedException notInThisExample ) {}
   }
   public synchronized void rehug()   {
      System.out.println( "I " + getName() + " am rehugged ");
   }
      
   public  void  run() {
      synchronized ( lock)   {
         aFriend.hug();
      }
   }
}

Doc 21, Threads part 3 Slide # 10
//Deadlock Avoided Continued

public class Test
   {
   public static void main( String args[] ) //throws Exception
      {
      Object aLock = "Schalage";
      Friendly  fred  =  new Friendly("Fred", aLock);
      Friendly  sam = new Friendly( "Sam", aLock);
      fred.setFriend( sam );
      sam.setFriend( fred );
      fred.start();
      sam.start();
      System.out.println( "End" );
      }
   }
Output
End
I Sam am hugged
I Fred am rehugged
I Fred am hugged
I Sam am rehugged


Listen Here!S-nov9 2mins Doc 21, Threads part 3 Slide # 11

Synchronized and Inheritance

If you want a method in a subclass to be synchronized you must declare it to be synchronized.

class Top
   {
   public void synchronized left()
      {
      // do stuff
      }
   public void synchronized right()
      {
      // do stuff
      }
   }
class Bottom extends Top
   {
   public void left()
      {
      // not synchronized 
      }
   public void right()
      {
      // do stuff not synchronized
      super.right();   // synchronized here
      // do stuff not synchronized
      }

Doc 21, Threads part 3 Slide # 12

Volatile

Java allows threads that access shared variables to keep private working copies of the variables. This improves the performance of multiple threaded programs. These working copies are reconciled with the master copies in shared main memory when objects are locked or unlocked. If you do not wish to use synchronized, Java has a second method to make sure that threads are using the proper value of shared variables. If a field is declared volatile, then a thread must reconcile its working copy of the field every time it accesses the variable. Operations on the master copy of the variable are performed in exactly the order that the thread requested. In the example on the next slide, a threads copy of the field "value" can get out of synch with its actual value.


Listen Here!S-nov9 4mins Doc 21, Threads part 3 Slide # 13
Volatile Example
class ExampleFromTheBook {
   int value;
   volatile int volatileValue;
   
   public void setValue( int newValue ) {
      value = newValue;
      volatileValue = newValue;
   }
   
   public void display() {
      value = 5;
      volatileValue = 5;
      
      for ( int  k = 0; k < 5; k++ ) {
         System.out.println( "Value " + value );
         System.out.println("Volatile " + volatileValue );
         Thread.yield( );
      }
   }
}

class ChangeValue extends Thread {
   ExampleFromTheBook myData;
   
   public ChangeValue( ExampleFromTheBook data ) {
      myData = data;
   }
   
   public void run() {
      for ( int  k = 0; k < 5; k++ ) {
         myData.value = k;
         myData.volatileValue = k;
         Thread.yield( );
      }
   }
}

public class Test  {
   public static void main( String args[] ) {
   
   ExampleFromTheBook example = new ExampleFromTheBook();
   ChangeValue changer = new ChangeValue( example );
   changer.start();
   
   example.display();
   }
}
Some of the Output
Value 5
Volatile 1
Value 2
Volatile 2
Value 3
Volatile 3

Listen Here!S-nov9 2mins Doc 21, Threads part 3 Slide # 14

wait and notify Methods in Object

wait and notify are some of the most useful thread operations.

public final void wait(timeout) throws InterruptedException

Causes a thread to wait until it is notified or the specified timeout expires.

Parameters:
timeout - the maximum time to wait in milliseconds
Throws: IllegalMonitorStateException
If the current thread is not the owner of the Object's monitor.
Throws: InterruptedException
Another thread has interrupted this thread.

public final void wait(timeout, nanos) throws InterruptedException
public final void wait() throws InterruptedException

public final void notify()
public final void notifyAll()

Notifies all of the threads waiting for a condition to change. Threads that are waiting are generally waiting for another thread to change some condition. Thus, the thread effecting a change that more than one thread is waiting for notifies all the waiting threads using the method notifyAll(). Threads that want to wait for a condition to change before proceeding can call wait(). The method notifyAll() can only be called from within a synchronized method.



Listen Here!S-nov9 5mins, S-nov11 26secs Doc 21, Threads part 3 Slide # 15

wait - How to use


The thread waiting for a condition should look like:

synchronized void waitingMethod()
   {
   while ( ! condition )
      wait();
   Now do what you need to do when condition is true
   }

Note

Everything is executed in a synchronized method

The test condition is in loop not in an if statement

The wait suspends the thread it atomically releases the lock on the object

Listen Here!S-nov11 40secs Doc 21, Threads part 3 Slide # 16

notify - How to Use


synchronized void changeMethod()
   {
   Change some value used in a condition test
   notify();
   }

Listen Here!S-nov11 7mins Doc 21, Threads part 3 Slide # 17
wait and notify Example
Over the next five slides is a typical consumer-producer example. Producers "make" items, which they put into a queue. Consumers remove items from the queue. What happens when the consumer wishes to remove when the queue is empty? Using threads, we can have the consumer thread wait until a producer thread adds items to the queue.
class Queue {
   LinkedCell head;
   LinkedCell tail;
   
   public synchronized void append( Object item ) {
      LinkedCell  newItem = new LinkedCell( item );
      if ( head == null )
         head = newItem;
      else
         tail.append( newItem );
      tail = newItem;
      notify();
   }
   public synchronized Object get( ) {
      try {
         while ( head == null )
            wait();
      }
      catch (InterruptedException threadIsDone ) {
         return null;
      }
      
      Object returnMe = head.element();
      head = head.next();
      return returnMe;
   }
}

Doc 21, Threads part 3 Slide # 18
wait and notify - Helper

class LinkedCell
   {
   Object cellItem;
   LinkedCell next;
   public LinkedCell( Object value )
      {
      cellItem= value;
      }
   public Object element()
      { 
      return cellItem;
      }
   public void append( LinkedCell nextCell )
      {
      next = nextCell;
      }
   public LinkedCell next()
      { 
      return next; 
      }
   }

Listen Here!S-nov11 2mins Doc 21, Threads part 3 Slide # 19
wait and notify - Producer

class Producer extends Thread
   {
   Queue factory;
   int workSpeed;
   
   public Producer( String name, Queue output, int speed )
      {
      setName(name);
      factory = output;
      workSpeed = speed;
      }
   
   public void run()
      {
      try
         {
         int product = 0;
         while (true) // work forever
            {
            System.out.println( getName() + " produced " + product);
            factory.append( getName() + String.valueOf( product) );
            product++;
            sleep( workSpeed);
            }
         }
      catch ( InterruptedException WorkedToDeath )
         { 
         return;
         }
      }
   }

Listen Here!S-nov11 1min Doc 21, Threads part 3 Slide # 20
wait and notify - Consumer

class Consumer extends Thread
   {
   Queue localMall;
   int sleepDuration;
   
   public Consumer( String name, Queue input, int speed )
      {
      setName(name);
      localMall = input;
      sleepDuration = speed;
      }
   
   public void run()
      {
      try
         {
         while (true) // Shop until you drop
            {
            System.out.println( getName() + " got " + 
                              localMall.get());
            sleep( sleepDuration );
            }
         }
      catch ( InterruptedException endOfCreditCard )
         { 
         return;
         }
      }
   }

Listen Here!S-nov11 3mins Doc 21, Threads part 3 Slide # 21
wait and notify - Driver Program

class Test
   {
   public  static  void  main( String  args[] ) throws Exception
      {
      Queue wallmart = new Queue();
      Producer nike = new Producer( "Nike", wallmart, 500 );
      Producer honda = new Producer( "Honda", wallmart, 1200 );
      Consumer valleyGirl = new Consumer( "Sue", wallmart, 400);
      Consumer valleyBoy = new Consumer( "Bob", wallmart, 900);
      Consumer dink = new Consumer( "Sam", wallmart, 2200);
      nike.start();
      honda.start();
      valleyGirl.start();
      valleyBoy.start();
      dink.start();
      }
   }
Output

Nike produced 0Sue got Nike3Honda produced 3
Honda produced 0Nike produced 4Bob got Honda3
Sue got Nike0Sue got Nike4Nike produced 8
Bob got Honda0Honda produced Sue got Nike8
Nike produced 1Bob got Honda2Nike produced 9
Sam got Nike1Nike produced 5Sue got Nike9
Nike produced 2Sue got Nike5Honda produced 4
Sue got Nike2Nike produced 6Bob got Honda4
Honda produced 1Sam got Nike6Nike produced 10
Bob got Honda1Nike produced 7Sue got Nike10
Nike produced 3Sue got Nike7Nike produced 11


Listen Here!S-nov11 4mins Doc 21, Threads part 3 Slide # 22

Piped Streams & Threads


In most streams, one end of the stream is "connected" to a file, socket, keyboard, etc. With piped streams, both ends are in your program. This allows one thread to write data via a stream to another thread in your program. The following example illustrates this.



class  TestIO {
   public static void main( String[]  args )  throws IOException {
      PipedReader  inPipe  =  new PipedReader();
      PipedWriter  outPipe  =  new PipedWriter(inPipe);
      PrintWriter  cout  =  new  PrintWriter(  outPipe  );
      ReadThread  reader  =  new ReadThread( "Read",  inPipe  );
      reader.setPriority( 6 );   // 5 is normal priority
      reader.start();
      System.out.println( "In Main" );
      cout.println( "Hello" );
      System.out.println( "End" );
   }
}

Listen Here!S-nov11 2mins Doc 21, Threads part 3 Slide # 23
Messages between Threads

import java.io.*;
import sdsu.io.UnicodeReader;

class  ReadThread  extends Thread {
   private  UnicodeReader  cin;
   public  ReadThread(  String  name,  PipedReader  input ) {
      super( name );
      cin  =  new UnicodeReader( input );
   }
   public  void run() {
      try {
         System.out.println( "Start " + getName() );
         String  message  =  cin.readWord();
         System.out.println( message +  " From: " + getName() );
      } catch  ( Exception  ignored ) {}
   }
}
Output
Start Read
In Main
End
Hello From: Read

Doc 21, Threads part 3 Slide # 24

ThreadLocal Variables

JDK 1.2 introduces ThreadLocal variables. Here's what the on-line docs say about them:

"These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal objects are typically private static variables in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).

Each thread holds an implicit reference to its copy of a ThreadLocal as long as the thread is alive and the ThreadLocal object is accessible; after a thread goes away, all of its copies of ThreadLocal variables are subject to garbage collection (unless other references to these copies exist)."

The following two slides give examples of the use of ThreadLocal variables. The key to understanding the examples is:

1) Each thread has its own copy of the ThreadLocal value

2) The initial value for each thread is null

3) In deciding which thread to assign the value to, it does not matter what code sets the value of the ThreadLocal, what matters in what thread was running.

Listen Here!S-nov11 12mins, S-nov11 6mins Doc 21, Threads part 3 Slide # 25
ThreadLocal Example Correct Way
class LocalExample extends Thread {
   private static ThreadLocal name = new ThreadLocal();
   
   LocalExample myFriend;
   
   public LocalExample( String myName ) {super( myName ); }
   
   public void run() {
      name.set( getName() );
      System.out.println( "My name " + name() );
      System.out.println( "My friend's name " + myFriend.name() );
   }
   
   public String name() { return (String) name.get(); }
   public void setFriend( LocalExample anExample ) {
      myFriend = anExample;
   }
}
public class Test  {
   public static void main(String args[]) throws InterruptedException{
      LocalExample a = new LocalExample( "A" );
      LocalExample b = new LocalExample( "B" );
      a.setFriend( b );
      b.setFriend( a );
      a.start();
      b.start();
      a.join();
      b.join();
      System.out.println( "A's name " + a.name() );
   }
}
Output
My name B
My friend's name B
My name A
My friend's name A
A's name null

Listen Here!S-nov11 3mins Doc 21, Threads part 3 Slide # 26
ThreadLocal Example Wrong Way
class LocalExample extends Thread {
   private static ThreadLocal name = new ThreadLocal();
   
   LocalExample myFriend;
   
   public LocalExample( String myName ) { name.set( myName );}
   
   public void run() {
      System.out.println( "My name " + name() );
      System.out.println( "My friend's name " + myFriend.name() );
   }
   
   public String name() {   return (String) name.get(); }
   public void setFriend( LocalExample anExample ) {
      myFriend = anExample;
   }
}
public class Test  {
   public static void main(String args[]) throws InterruptedException{
      LocalExample a = new LocalExample( "A" );
      LocalExample b = new LocalExample( "B" );
      a.setFriend( b );
      b.setFriend( a );
      a.start();
      b.start();
      a.join();
      b.join();
      System.out.println( "A's name " + a.name() );
   }
}
Output
My name null
My friend's name null
My name null
My friend's name null
A's name B

Doc 21, Threads part 3 Slide # 27
InheritableThreadLocal Variables

These act like process variables in Unix. Child threads automatically get a copy of the parent's threads values.

From the on-line documentation:

"This class extends ThreadLocal to provide inheritance of values from parent Thread to child Thread: when a child thread is created, the child receives initial values for all InheritableThreadLocals for which the parent has values. Normally the child's values will be identical to the parent's; however, the child's value can be made an arbitrary function of the parent's by overriding the childValue method in this class."


Listen Here!S-nov11 6mins Doc 21, Threads part 3 Slide # 28
InheritableThreadLocal Variables Example
class LocalExample extends Thread {
   private static InheritableThreadLocal name = 
      new InheritableThreadLocal();
   private static InheritableThreadLocal id = 
      new InheritableThreadLocal();
   private static ThreadLocal color = new ThreadLocal();
   static int count = 0;
   
   public void run() {
      if ( count == 0 ) {
         count++;
         name.set( "A" );
         id.set( new Integer( count ));
         color.set( "Green" );
         System.out.println( "Parent " + name.get() + " " + id.get() + " " 
                                                   + color.get() );
         LocalExample child = new LocalExample();
         child.start();
      }
      else if ( count > 0 ) {
         System.out.println( "Child " + name.get() + " " + id.get() + " " 
                                                   + color.get() );
      }
   }
}
public class Test  {
   public static void main(String args[]) {
      LocalExample a = new LocalExample(  );
      a.start();
   }
}
Output
Parent A 1 Green
Child A 1 null

Doc 21, Threads part 3 Slide # 29
Debugging Threads

Some useful methods in Thread for debugging


public static void dumpStack()

Prints a stack trace for the current thread on System.out


public String toString()

Returns a String representation of the Thread, including the thread's name, priority and thread group.


public int countStackFrames()

Returns the number of stack frames in this Thread. The Thread must be suspended when this method is called.

Copyright © 1998 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
All rights reserved.

visitors since 04-Nov-98