Blocking

Bruce Eckel's Thinking in Java Contents | Prev | Next

  1. New: the thread object has been created but it hasn’t been started yet so it cannot run.
  2. Runnable: This means that a thread can be run when the time-slicing mechanism has CPU cycles available for the thread. Thus, the thread might or might not be running, but there’s nothing to prevent it from being run if the scheduler can arrange it; it’s not dead or blocked.
  3. Dead: the normal way for a thread to die is by returning from its run( ) method. You can also call stop( ), but this throws an exception that’s a subclass of Error (which means you usually don’t catch it). Remember that throwing an exception should be a special event and not part of normal program execution; thus the use of stop( ) is discouraged (and it’s deprecated in Java 1.2). There’s also a destroy( ) method (which has never been implemented) that you should never call if you can avoid it since it’s drastic and doesn’t release object locks.
  4. Blocked: the thread could be run but there’s something that prevents it. While a thread is in the blocked state the scheduler will simply skip over it and not give it any CPU time. Until a thread re-enters the runnable state it won’t perform any operations.

Becoming blocked

The blocked state is the most interesting and is worth further examination. A thread can become blocked for five reasons:

  1. You’ve put the thread to sleep by calling sleep(milliseconds), in which case it will not be run for the specified time.
  2. You’ve suspended the execution of the thread with suspend( ). It will not become runnable again until the thread gets the resume( ) message.
  3. You’ve suspended the execution of the thread with wait( ). It will not become runnable again until the thread gets the notify( ) or notifyAll( ) message. (Yes, this looks just like number 2, but there’s a distinct difference that will be revealed.)
  4. The thread is waiting for some IO to complete.
  5. The thread is trying to call a synchronized method on another object and that object’s lock is not available.
//: Blocking.java
// Demonstrates the various ways a thread
// can be blocked.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import java.io.*;
 
//////////// The basic framework ///////////
class Blockable extends Thread {
  private Peeker peeker;
  protected TextField state = new TextField(40);
  protected int i;
  public Blockable(Container c) {
    c.add(state);
    peeker = new Peeker(this, c);
  }
  public synchronized int read() { return i; }
  protected synchronized void update() {
    state.setText(getClass().getName()
      + " state: i = " + i);
  }
  public void stopPeeker() { 
    // peeker.stop(); Deprecated in Java 1.2
    peeker.terminate(); // The preferred approach
  }
}
 
class Peeker extends Thread {
  private Blockable b;
  private int session;
  private TextField status = new TextField(40);
  private boolean stop = false;
  public Peeker(Blockable b, Container c) {
    c.add(status);
    this.b = b;
    start();
  }
  public void terminate() { stop = true; }
  public void run() {
    while (!stop) {
      status.setText(b.getClass().getName()
        + " Peeker " + (++session)
        + "; value = " + b.read());
       try {
        sleep(100);
      } catch (InterruptedException e){}
    }
  }
} ///:Continued 

The Blockable class is meant to be a base class for all the classes in this example that demonstrate blocking. A Blockable object contains a TextField called state that is used to display information about the object. The method that displays this information is update( ). You can see it uses getClass( ).getName( ) to produce the name of the class instead of just printing it out; this is because update( ) cannot know the exact name of the class it is called for, since it will be a class derived from Blockable.

The indicator of change in Blockable is an int i, which will be incremented by the run( ) method of the derived class.

There’s a thread of class Peeker that is started for each Blockable object, and the Peeker’s job is to watch its associated Blockable object to see changes in i by calling read( ) and reporting them in its status TextField . This is important: Note that read( ) and update( ) are both synchronized, which means they require that the object lock be free.

Sleeping
///:Continuing
///////////// Blocking via sleep() ///////////
class Sleeper1 extends Blockable {
  public Sleeper1(Container c) { super(c); }
  public synchronized void run() {
    while(true) {
      i++;
      update();
       try {
        sleep(1000);
      } catch (InterruptedException e){}
    }
  }
}
 
class Sleeper2 extends Blockable {
  public Sleeper2(Container c) { super(c); }
  public void run() {
    while(true) {
      change();
       try {
        sleep(1000);
      } catch (InterruptedException e){}
    }
  }
  public synchronized void change() {
      i++;
      update();
  }
} ///:Continued 

In Sleeper1 the entire run( ) method is synchronized. You’ll see that the Peeker associated with this object will run along merrily until you start the thread, and then the Peeker stops cold. This is one form of blocking: since Sleeper1.run( ) is synchronized, and once the thread starts it’s always inside run( ), the method never gives up the object lock and the Peeker is blocked.

Sleeper2 provides a solution by making run un- synchronized. Only the change( ) method is synchronized, which means that while run( ) is in sleep( ), the Peeker can access the synchronized method it needs, namely read( ). Here you’ll see that the Peeker continues running when you start the Sleeper2 thread.

Suspending and resuming
///:Continuing
/////////// Blocking via suspend() ///////////
class SuspendResume extends Blockable {
  public SuspendResume(Container c) {
    super(c);    
    new Resumer(this); 
  }
}
 
class SuspendResume1 extends SuspendResume {
  public SuspendResume1(Container c) { super(c);}
  public synchronized void run() {
    while(true) {
      i++;
      update();
      suspend(); // Deprecated in Java 1.2
    }
  }
}
 
class SuspendResume2 extends SuspendResume {
  public SuspendResume2(Container c) { super(c);}
  public void run() {
    while(true) {
      change();
      suspend(); // Deprecated in Java 1.2
    }
  }
  public synchronized void change() {
      i++;
      update();
  }
}
 
class Resumer extends Thread {
  private SuspendResume sr;
  public Resumer(SuspendResume sr) {
    this.sr = sr;
    start();
  }
  public void run() {
    while(true) {
       try {
        sleep(1000);
      } catch (InterruptedException e){}
      sr.resume(); // Deprecated in Java 1.2
    }
  }
} ///:Continued 

SuspendResume1 also has a synchronized run( ) method. Again, when you start this thread you’ll see that its associated Peeker gets blocked waiting for the lock to become available, which never happens. This is fixed as before in SuspendResume2, which does not synchronize the entire run( ) method but instead uses a separate synchronized change( ) method.

Wait and notify
One fairly unique aspect of wait( ) and notify( ) is that both methods are part of the base class Object and not part of Thread as are sleep( ), suspend( ), and resume( ). Although this seems a bit strange at first – to have something that’s exclusively for threading as part of the universal base class – it’s essential because they manipulate the lock that’s also part of every object. As a result, you can put a wait( ) inside any synchronized method, regardless of whether there’s any threading going on inside that particular class. In fact, the only place you can call wait( ) is within a synchronized method or block. If you call wait( ) or notify( ) within a method that’s not synchronized, the program will compile, but when you run it you’ll get an IllegalMonitorStateException with the somewhat non-intuitive message “current thread not owner.” Note that sleep( ), suspend( ), and resume( ) can all be called within non- synchronized methods since they don’t manipulate the lock.

synchronized(wn2) {
  wn2.notify();
}

where wn2 is the object of type WaitNotify2. This method, which is not part of WaitNotify2, acquires the lock on the wn2 object, at which point it’s legal for it to call notify( ) for wn2 and you won’t get the IllegalMonitorStateException.

///:Continuing
/////////// Blocking via wait() ///////////
class WaitNotify1 extends Blockable {
  public WaitNotify1(Container c) { super(c); }
  public synchronized void run() {
    while(true) {
      i++;
      update();
       try {
        wait(1000);
      } catch (InterruptedException e){}
    }
  }
}
 
class WaitNotify2 extends Blockable {
  public WaitNotify2(Container c) {
    super(c);
    new Notifier(this); 
  }
  public synchronized void run() {
    while(true) {
      i++;
      update();
       try {
        wait();
      } catch (InterruptedException e){}
    }
  }
}
 
class Notifier extends Thread {
  private WaitNotify2 wn2;
  public Notifier(WaitNotify2 wn2) {
    this.wn2 = wn2;
    start();
  }
  public void run() {
    while(true) {
       try {
        sleep(2000);
      } catch (InterruptedException e){}
      synchronized(wn2) {
        wn2.notify();
      }
    }
  }
} ///:Continued 

wait( ) is typically used when you’ve gotten to the point where you’re waiting for some other condition, under the control of forces outside your thread, to change and you don’t want to idly wait by inside the thread. So wait( ) allows you to put the thread to sleep while waiting for the world to change, and only when a notify( ) or notifyAll( ) occurs does the thread wake up and check for changes. Thus, it provides a way to synchronize between threads.

Blocking on IO

///:Continuing
class Sender extends Blockable { // send
  private Writer out;
  public Sender(Container c, Writer out) { 
    super(c);
    this.out = out; 
  }
  public void run() {
    while(true) {
      for(char c = 'A'; c <= 'z'; c++) {
         try {
          i++;
          out.write(c);
          state.setText("Sender sent: " 
            + (char)c);
          sleep((int)(3000 * Math.random()));
        } catch (InterruptedException e){}
          catch (IOException e) {}
      }
    }
  }
}
 
class Receiver extends Blockable {
  private Reader in;
  public Receiver(Container c, Reader in) { 
    super(c);
    this.in = in; 
  }
  public void run() {
    try {
      while(true) {
        i++; // Show peeker it's alive
        // Blocks until characters are there:
        state.setText("Receiver read: "
          + (char)in.read());
      }
    } catch(IOException e) { e.printStackTrace();}
  }
} ///:Continued 

Both classes also put information into their state fields and change i so the Peeker can see that the thread is running.

Testing
///:Continuing
/////////// Testing Everything ///////////
public class Blocking extends Applet {
  private Button 
    start = new Button("Start"),
    stopPeekers = new Button("Stop Peekers");
  private boolean started = false;
  private Blockable[] b;
  private PipedWriter out;
  private PipedReader in;
  public void init() {
     out = new PipedWriter();
    try {
      in = new PipedReader(out);
    } catch(IOException e) {}
    b = new Blockable[] {
      new Sleeper1(this),
      new Sleeper2(this),
      new SuspendResume1(this),
      new SuspendResume2(this),
      new WaitNotify1(this),
      new WaitNotify2(this),
      new Sender(this, out),
      new Receiver(this, in)
    };
    start.addActionListener(new StartL());
    add(start);
    stopPeekers.addActionListener(
      new StopPeekersL());
    add(stopPeekers);
  }
  class StartL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      if(!started) {
        started = true;
        for(int i = 0; i < b.length; i++)
          b[i].start();
      }
    }
  }
  class StopPeekersL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      // Demonstration of the preferred 
      // alternative to Thread.stop():
      for(int i = 0; i < b.length; i++)
        b[i].stopPeeker();
    }
  }
  public static void main(String[] args) {
    Blocking applet = new Blocking();
    Frame aFrame = new Frame("Blocking");
    aFrame.addWindowListener(
      new WindowAdapter() {
        public void windowClosing(WindowEvent e) {
          System.exit(0);
        }
      });
    aFrame.add(applet, BorderLayout.CENTER);
    aFrame.setSize(350,550);
    applet.init();
    applet.start();
    aFrame.setVisible(true);
  }
} ///:~ 

In init( ), notice the loop that moves through the entire array and adds the state and peeker.status text fields to the page.

When the Blockable threads are initially created, each one automatically creates and starts its own Peeker. So you’ll see the Peekers running before the Blockable threads are started. This is essential, as some of the Peekers will get blocked and stop when the Blockable threads start, and it’s essential to see this to understand that particular aspect of blocking.

Deadlock

Because threads can become blocked and because objects can have synchronized methods that prevent threads from accessing that object until the synchronization lock is released, it’s possible for one thread to get stuck waiting for another thread, which in turn waits for another thread, etc., until the chain leads back to a thread waiting on the first one. Thus, there’s a continuous loop of threads waiting on each other and no one can move. This is called deadlock. The claim is that it doesn’t happen that often, but when it happens to you it’s frustrating to debug.

There is no language support to help prevent deadlock; it’s up to you to avoid it by careful design. These are not comforting words to the person who’s trying to debug a deadlocking program.

The deprecation of stop( ), suspend( ),

resume( ), and destroy( ) in Java 1.2

//: Interrupt.java
// The alternative approach to using stop()
// when a thread is blocked
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
 
class Blocked extends Thread {
  public synchronized void run() {
    try {
      wait(); // Blocks
    } catch(InterruptedException e) {
      System.out.println("InterruptedException");
    }
    System.out.println("Exiting run()");
  }
}
 
public class Interrupt extends Applet {
  private Button 
    interrupt = new Button("Interrupt");
  private Blocked blocked = new Blocked();
  public void init() {
    add(interrupt);
    interrupt.addActionListener(
      new ActionListener() {
        public 
        void actionPerformed(ActionEvent e) {
          System.out.println("Button pressed");
          if(blocked == null) return;
          Thread remove = blocked;
          blocked = null; // to release it
          remove.interrupt();
        }
      });
    blocked.start();
  }
  public static void main(String[] args) {
    Interrupt applet = new Interrupt();
    Frame aFrame = new Frame("Interrupt");
    aFrame.addWindowListener(
      new WindowAdapter() {
        public void windowClosing(WindowEvent e) {
          System.exit(0);
        }
      });
    aFrame.add(applet, BorderLayout.CENTER);
    aFrame.setSize(200,100);
    applet.init();
    applet.start();
    aFrame.setVisible(true);
  }
} ///:~ 

The wait( ) inside Blocked.run( ) produces the blocked thread. When you press the button, the blocked handle is set to null so the garbage collector will clean it up, and then the object’s interrupt( ) method is called. The first time you press the button you’ll see that the thread quits, but after that there’s no thread to kill so you just see that the button has been pressed.

The suspend( ) and resume( ) methods turn out to be inherently deadlock-prone. When you call suspend( ), the target thread stops but it still holds any locks that it has acquired up to that point. So no other thread can access the locked resources until the thread is resumed. Any thread that wants to resume the target thread and also tries to use any of the locked resources produces deadlock. You should not use suspend( ) and resume( ), but instead put a flag in your Thread class to indicate whether the thread should be active or suspended. If the flag indicates that the thread is suspended, the thread goes into a wait using wait( ). When the flag indicates that the thread should be resumed the thread is restarted with notify( ). An example can be produced by modifying Counter2.java. Although the effect is similar, you’ll notice that the code organization is quite different – anonymous inner classes are used for all of the listeners and the Thread is an inner class, which makes programming slightly more convenient since it eliminates some of the extra bookkeeping necessary in Counter2.java:

//: Suspend.java
// The alternative approach to using suspend()
// and resume(), which have been deprecated
// in Java 1.2.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
 
public class Suspend extends Applet {
  private TextField t = new TextField(10);
  private Button 
    suspend = new Button("Suspend"),
    resume = new Button("Resume");
  class Suspendable extends Thread {
    private int count = 0;
    private boolean suspended = false;
    public Suspendable() { start(); }
    public void fauxSuspend() { 
      suspended = true;
    }
    public synchronized void fauxResume() {
      suspended = false;
      notify();
    }
    public void run() {
      while (true) {
        try {
          sleep(100);
          synchronized(this) {
            while(suspended)
              wait();
          }
        } catch (InterruptedException e){}
        t.setText(Integer.toString(count++));
      }
    }
  } 
  private Suspendable ss = new Suspendable();
  public void init() {
    add(t);
    suspend.addActionListener(
      new ActionListener() {
        public 
        void actionPerformed(ActionEvent e) {
          ss.fauxSuspend();
        }
      });
    add(suspend);
    resume.addActionListener(
      new ActionListener() {
        public 
        void actionPerformed(ActionEvent e) {
          ss.fauxResume();
        }
      });
    add(resume);
  }
  public static void main(String[] args) {
    Suspend applet = new Suspend();
    Frame aFrame = new Frame("Suspend");
    aFrame.addWindowListener(
      new WindowAdapter() {
        public void windowClosing(WindowEvent e){
          System.exit(0);
        }
      });
    aFrame.add(applet, BorderLayout.CENTER);
    aFrame.setSize(300,100);
    applet.init();
    applet.start();
    aFrame.setVisible(true);
  }
} ///:~ 

The flag suspended inside Suspendable is used to turn suspension on and off. To suspend, the flag is set to true by calling fauxSuspend( ) and this is detected inside run( ). The wait( ), as described earlier in this chapter, must be synchronized so that it has the object lock. In fauxResume( ), the suspended flag is set to false and notify( ) is called – since this wakes up wait( ) inside a synchronized clause the fauxResume( ) method must also be synchronized so that it acquires the lock before calling notify( ) (thus the lock is available for the wait( ) to wake up with). If you follow the style shown in this program you can avoid using wait( ) and notify( ).

You might wonder why these methods, now deprecated, were included in Java in the first place. It seems a clear admission of a rather significant mistake to simply remove them outright (and pokes yet another hole in the arguments for Java’s exceptional design and infallibility touted by Sun marketing people). The heartening part about the change is that it clearly indicates that the technical people and not the marketing people are running the show – they discovered a problem and they are fixing it. I find this much more promising and hopeful than leaving the problem in because fixing it would admit an error. It means that Java will continue to improve, even if it means a little discomfort on the part of Java programmers. I’d rather deal with the discomfort than watch the language stagnate.



Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • On-demand Event Event Date: September 10, 2014 Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild." This loop of continuous delivery and continuous feedback is how the best mobile …

  • Not all enterprise applications are created equal. Sophisticated applications need developer support but other more basic apps do not. With the right tools, everyone is a potential app developer with ideas and a perspective to share. Trends such as low-code development and model driven development are fundamentally changing how and who creates applications. Is your organization ready? Read this report and learn: The seven personas of enterprise app delivery How application ownership is spreading to the …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds