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.
The
following example shows all five ways of becoming blocked. It all exists in a
single file called
Blocking.java,
but it will be examined here in discrete pieces. (You’ll notice the
“Continued” and “Continuing” tags that allow the tool
shown in Chapter 17 to piece everything together.) First, the basic framework:

//: 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

You’ll
also see that there are two forms of
wait( ).
The first takes an argument in milliseconds that has the same meaning as in
sleep( ):
pause for this period of time. The difference is that in
wait( ),
the object lock is released
and
you can come out of the
wait( )
because of a
notify( )
as well as having the clock run out.

The
second form takes no arguments, and means that the
wait( )
will continue until a
notify( )
comes along and will not automatically terminate after a time.

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.

You
can call
wait( )
or
notify( )
only for your own lock. Again, you can compile code that tries to use the wrong
lock, but it will produce the same
IllegalMonitorStateException
message as before. You can’t fool with someone else’s lock, but you
can ask another object to perform an operation that manipulates its own lock.
So one approach is to create a
synchronized
method that calls
notify( )
for its own object. However, in
Notifier
you’ll see the
notify( )
call inside a
synchronized
block:

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

The
Sender
puts data into the
Writer
and sleeps for a random amount of time. However,
Receiver
has no
sleep( ),
suspend( ),
or
wait( ).
But when it does a
read( )
it automatically blocks when there is no more data.

///: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

To
set up a connection between the
Sender
and
Receiver
objects, a
PipedWriter
and
PipedReader
are created. Note that the
PipedReader
in
must
be connected to the
PipedWriter
out
via
a constructor argument. After that, anything that’s placed in
out
can later be extracted from
in,
as if it passed through a pipe (hence the name). The
in
and
out
objects are then passed to the
Receiver
and
Sender
constructors, respectively, which treat them as
Reader
and
Writer
objects
of any type (that is, they are upcast).

The
array of
Blockable
handles
b
is not initialized at its point of definition because the piped streams cannot
be set up before that definition takes place (the need for the
try
block prevents this).

///: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.

More by Author

Must Read