Responsive user interfaces | CodeGuru

Responsive user interfaces

Bruce Eckel’s Thinking in Java Contents | Prev | Next As a starting point, consider a program that performs some CPU-intensive operation and thus ends up ignoring user input and being unresponsive. This one, a combined applet/application, will simply display the result of a running counter: //: Counter1.java // A non-responsive user interface package c14; […]

Written By
CodeGuru Staff
CodeGuru Staff
Mar 1, 2001
13 minute read
CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More

As


a starting point, consider a program that performs some CPU-intensive operation


and thus ends up ignoring user input and being unresponsive. This one, a


combined applet/application, will simply display the result of a running counter:

//: Counter1.java
// A non-responsive user interface
package c14;
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
 
public class Counter1 extends Applet {
  private int count = 0;
  private Button
    onOff = new Button("Toggle"),
    start = new Button("Start");
  private TextField t = new TextField(10);
  private boolean runFlag = true;
  public void init() {
    add(t);
    start.addActionListener(new StartL());
    add(start);
    onOff.addActionListener(new OnOffL());
    add(onOff);
  }
  public void go() {
    while (true) {
      try {
        Thread.currentThread().sleep(100);
      } catch (InterruptedException e){}
      if(runFlag)
        t.setText(Integer.toString(count++));
    }
  }
  class StartL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      go();
    }
  }
  class OnOffL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      runFlag = !runFlag;
    }
  }
  public static void main(String[] args) {
    Counter1 applet = new Counter1();
    Frame aFrame = new Frame("Counter1");
    aFrame.addWindowListener(
      new WindowAdapter() {
        public void windowClosing(WindowEvent e) {
          System.exit(0);
        }
      });
    aFrame.add(applet, BorderLayout.CENTER);
    aFrame.setSize(300,200);
    applet.init();
    applet.start();
    aFrame.setVisible(true);
  }
} ///:~ 

At


this point, the AWT and applet code should be reasonably familiar from Chapter


13. The


go( )

method is where the program stays busy: it puts the current value of


count

into the


TextField
t

,


then increments


count

.

Part


of the infinite loop inside


go( )

is to call


sleep( )

.


sleep( )
must be associated with a
Thread
object, and it turns out that every application has
some
thread associated with it. (Indeed, Java is based on threads and there are
always some running along with your application.) So regardless of whether
you’re explicitly using threads, you can produce the current thread used
by your program with
Thread.
currentThread()

(a static method of the
Thread
class)
and then call
sleep( )
for that thread.

Note


that


sleep( )

can throw

InterruptedException,
although throwing such an exception is considered a hostile way to break from a
thread and should be discouraged. (Once again, exceptions are for exceptional
conditions, not normal flow of control.) Interrupting a sleeping thread is
included to support a future language feature.

When


the


start

button is pressed,


go( )

is invoked. And upon examining


go( )

,


you might naively think (as I did) that it should allow multithreading because


it goes to sleep. That is, while the method is asleep, it seems like the CPU


could be busy monitoring other button presses. But it turns out that the real


problem is that


go( )

never returns, since it’s in an infinite loop, and this means that


actionPerformed( )

never returns. Since you’re stuck inside


actionPerformed( )

for the first keypress, the program can’t handle any other events. (To


get out, you must somehow kill the process; the easiest way to do this is to


press Control-C in the console window.)

The


basic problem here is that


go( )

needs to continue performing its operations, and at the same time it needs to


return so


actionPerformed( )

can complete and the user interface can continue responding to the user. But in


a conventional method like


go( )

it cannot continue


and

at the same time return control to the rest of the program. This sounds like an


impossible thing to accomplish, as if the CPU must be in two places at once,


but this is precisely the illusion that threading provides. The thread model


(and programming support in Java) is a programming convenience to simplify


juggling several operations at the same time within a single program. With


threads, the CPU will pop around and give each thread some of its time. Each


thread has the consciousness of constantly having the CPU to itself, but the


CPU’s time is actually sliced between all the threads.

Threading
reduces computing efficiency somewhat, but the net improvement in program
design, resource balancing, and user convenience is often quite valuable. Of
course, if you have more than one CPU, then the operating system can dedicate
each CPU to a set of threads or even a single thread and the whole program can
run much faster. Multitasking and multithreading tend to be the most reasonable
ways to utilize multiprocessor systems.

Inheriting
from Thread

The


simplest way to create a thread is to inherit from class


Thread

,


which has all the wiring necessary to create and run threads. The most


important method for


Thread

is


run( )

,


which you must override to make the thread do your bidding. Thus,


run( )

is the code that will be executed “simultaneously” with the other


threads in a program.

The


following example creates any number of threads that it keeps track of by


assigning each thread a unique number, generated with a


static

variable. The

Thread’s
run( )
method is overridden to count down each time it passes through its loop and to
finish when the count is zero (at the point when
run( )
returns, the thread is terminated).
//: SimpleThread.java
// Very simple Threading example
 
public class SimpleThread extends Thread {
  private int countDown = 5;
  private int threadNumber;
  private static int threadCount = 0;
  public SimpleThread() {
    threadNumber = ++threadCount;
    System.out.println("Making " + threadNumber);
  }
  public void run() {
    while(true) {
      System.out.println("Thread " +
        threadNumber + "(" + countDown + ")");
      if(--countDown == 0) return;
    }
  }
  public static void main(String[] args) {
    for(int i = 0; i < 5; i++)
      new SimpleThread().start();
    System.out.println("All Threads Started");
  }
} ///:~ 

A


run( )

method virtually always has some kind of loop that continues until the thread


is no longer necessary, so you must establish the condition on which to break


out of this loop (or, in the case above, simply


return

from


run( )

).


Often,


run( )

is cast in the form of an infinite loop, which means that, barring some


external call to


stop( )

or


destroy( )

for that thread, it will run forever (until the program completes).

In


main( )

you


can see a number of threads being created and run. The special method that


comes with the

Thread
class is
start( ),
which performs special initialization for the thread and then calls
run( ).
So the steps are: the constructor is called to build the object, then
start( )
configures the thread and calls
run( ).
If you don’t call
start( )
(which you can do in the constructor, if that’s appropriate) the thread
will never be started.

The


output for one run of this program (it will be different every time) is:

Making 1
Making 2
Making 3
Making 4
Making 5
Thread 1(5)
Thread 1(4)
Thread 1(3)
Thread 1(2)
Thread 2(5)
Thread 2(4)
Thread 2(3)
Thread 2(2)
Thread 2(1)
Thread 1(1)
All Threads Started
Thread 3(5)
Thread 4(5)
Thread 4(4)
Thread 4(3)
Thread 4(2)
Thread 4(1)
Thread 5(5)
Thread 5(4)
Thread 5(3)
Thread 5(2)
Thread 5(1)
Thread 3(4)
Thread 3(3)
Thread 3(2)
Thread 3(1)

You’ll


notice that nowhere in this example is


sleep( )

called, and yet the output indicates that each thread gets a portion of the


CPU’s time in which to execute. This shows that


sleep( )

,


while it relies on the existence of a thread in order to execute, is not


involved with either enabling or disabling threading. It’s simply another


method.

You


can also see that the

threads
are not run in the order that they’re created. In fact, the order that
the CPU attends to an existing set of threads is indeterminate, unless you go
in and adjust the priorities using
Thread’s
setPriority( )
method.

When


main( )

creates the


Thread

objects it isn’t capturing the handles for any of them. An ordinary


object would be fair game for garbage collection, but not a


Thread

.


Each


Thread

“registers” itself so there is actually a reference to it someplace


and the garbage collector can’t clean it up.


Threading
for a responsive interface

Now


it’s possible to solve the problem in


Counter1.java

with


a thread. The trick is to place the subtask – that is, the loop


that’s inside


go( )

– inside the


run( )

method of a thread. When the user presses the


start

button, the thread is started, but then the


creation

of the thread completes, so even though the thread is running, the main job of


the program (watching for and responding to user-interface events) can


continue. Here’s the solution:

//: Counter2.java
// A responsive user interface with threads
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
 
class SeparateSubTask extends Thread {
  private int count = 0;
  private Counter2 c2;
  private boolean runFlag = true;
  public SeparateSubTask(Counter2 c2) {
    this.c2 = c2;
    start();
  }
  public void invertFlag() { runFlag = !runFlag;}
  public void run() {
    while (true) {
     try {
      sleep(100);
     } catch (InterruptedException e){}
     if(runFlag)
       c2.t.setText(Integer.toString(count++));
    }
  }
}
 
public class Counter2 extends Applet {
  TextField t = new TextField(10);
  private SeparateSubTask sp = null;
  private Button
    onOff = new Button("Toggle"),
    start = new Button("Start");
  public void init() {
    add(t);
    start.addActionListener(new StartL());
    add(start);
    onOff.addActionListener(new OnOffL());
    add(onOff);
  }
  class StartL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      if(sp == null)
        sp = new SeparateSubTask(Counter2.this);
    }
  }
  class OnOffL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      if(sp != null)
        sp.invertFlag();
    }
  }
  public static void main(String[] args) {
    Counter2 applet = new Counter2();
    Frame aFrame = new Frame("Counter2");
    aFrame.addWindowListener(
      new WindowAdapter() {
        public void windowClosing(WindowEvent e) {
          System.exit(0);
        }
      });
    aFrame.add(applet, BorderLayout.CENTER);
    aFrame.setSize(300,200);
    applet.init();
    applet.start();
    aFrame.setVisible(true);
  }
} ///:~ 
Counter2

is now a straightforward program, whose job is only to set up and maintain the


user interface. But now, when the user presses the


start

button, a method is not called. Instead a thread of class


SeparateSubTask

is created (the constructor starts it, in this case), and then the


Counter2

event loop continues. Note that the handle to the


SeparateSubTask

is stored so that when you press the


onOff

button it can toggle the


runFlag

inside the


SeparateSubTask

object. That thread (when it looks at the flag) can then start and stop itself.


(This could also have been accomplished by making


SeparateSubTask

an inner class.)

The


class


SeparateSubTask

is a simple extension of


Thread

with a constructor (that stores the


Counter2

handle and then runs the thread by calling


start( )

)


and a


run( )

that essentially contains the code from inside


go( )

in


Counter1.java

.


Because


SeparateSubTask

knows that it holds a handle to a


Counter2

,


it can reach in and access


Counter2

’s


TextField

when it needs to.

When


you press the


onOff

button, you’ll see a virtually instant response. Of course, the response


isn’t really instant, not like that of a system that’s driven by


interrupts. The counter stops only when the thread has the CPU and notices that


the flag has changed.



Improving
the code with an inner class

As


an aside, look at the coupling that occurs between the


SeparateSubTask

and


Counter2

classes. The


SeparateSubTask

is intimately tied to


Counter2

– it must keep a handle to its “parent”


Counter2

object so it can call back and manipulate it. And yet the two classes


shouldn’t really merge together into a single class (although in the next


section you’ll see that Java provides a way to combine them) because


they’re doing separate things and are created at different times. They


are tightly connected (what I call a “

couplet”)
and this makes the coding awkward. This is a situation in which an
inner
class can improve the code significantly:
//: Counter2i.java
// Counter2 using an inner class for the thread
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
 
public class Counter2i extends Applet {
  private class SeparateSubTask extends Thread {
    int count = 0;
    boolean runFlag = true;
    SeparateSubTask() { start(); }
    public void run() {
      while (true) {
       try {
        sleep(100);
       } catch (InterruptedException e){}
       if(runFlag)
         t.setText(Integer.toString(count++));
      }
    }
  }
  private SeparateSubTask sp = null;
  private TextField t = new TextField(10);
  private Button
    onOff = new Button("Toggle"),
    start = new Button("Start");
  public void init() {
    add(t);
    start.addActionListener(new StartL());
    add(start);
    onOff.addActionListener(new OnOffL());
    add(onOff);
  }
  class StartL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      if(sp == null)
        sp = new SeparateSubTask();
    }
  }
  class OnOffL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      if(sp != null)
        sp.runFlag = !sp.runFlag; // invertFlag();
    }
  }
  public static void main(String[] args) {
    Counter2i applet = new Counter2i();
    Frame aFrame = new Frame("Counter2i");
    aFrame.addWindowListener(
      new WindowAdapter() {
        public void windowClosing(WindowEvent e) {
          System.exit(0);
        }
      });
    aFrame.add(applet, BorderLayout.CENTER);
    aFrame.setSize(300,200);
    applet.init();
    applet.start();
    aFrame.setVisible(true);
  }
} ///:~ 

This


SeparateSubTask

name will not collide with the


SeparateSubTask

in the previous example even though they’re in the same directory, since


it’s hidden as an inner class. You can also see that the

inner
class is
private,
which means that its fields and methods can be given default access (except for
run( ),
which must be
public
since it is
public
in the base class). The
private
inner
class is not accessible to anyone but
Counter2i,
and since the two classes are tightly coupled it’s convenient to loosen
the access restrictions between them. In
SeparateSubTask
you can see that the
invertFlag( )
method has been removed since
Counter2i
can now directly access
runFlag.

Also,


notice that


SeparateSubTask

’s


constructor has been simplified – now it only starts the thread. The


handle to the


Counter2i

object is still being captured as in the previous version, but instead of doing


it by hand and referencing the outer object by hand, the inner class mechanism


takes care of it automatically. In


run( )

,


you can see that


t

is simply accessed, as if it were a field of


SeparateSubTask

.


The


t

field in the parent class can now be made


private

since


SeparateSubTask

can access it without getting any special permission – and it’s


always good to make fields “as private as possible” so they cannot


be accidentally changed by forces outside your class.

Anytime


you notice classes that appear to have high coupling with each other, consider


the coding and maintenance improvements you might get by using inner classes.


Advertisement

Combining
the thread

with
the main class

In


the example above you can see that the thread class is separate from the


program’s main class. This makes a lot of sense and is relatively easy to


understand. There is, however, an alternate form that you will often see used


that is not so clear but is usually more concise (which probably accounts for


its popularity). This form combines the main program class with the thread


class by making the main program class a thread. Since for a GUI program the


main program class must be inherited from either


Frame

or


Applet

,


an interface must be used to paste on the additional functionality. This


interface is called


Runnable

,


and it contains the same basic method that


Thread

does. In fact,


Thread

also implements


Runnable

,


which specifies only that there be a


run( )

method.

The


use of the combined program/thread is not quite so obvious. When you start the


program, you create an object that’s


Runnable

,


but you don’t start the thread. This must be done explicitly. You can see


this in the following program, which reproduces the functionality of


Counter2

:

//: Counter3.java
// Using the Runnable interface to turn the 
// main class into a thread.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
 
public class Counter3
    extends Applet implements Runnable {
  private int count = 0;
  private boolean runFlag = true;
  private Thread selfThread = null;
  private Button
    onOff = new Button("Toggle"),
    start = new Button("Start");
  private TextField t = new TextField(10);
  public void init() {
    add(t);
    start.addActionListener(new StartL());
    add(start);
    onOff.addActionListener(new OnOffL());
    add(onOff);
  }
  public void run() {
    while (true) {
      try {
        selfThread.sleep(100);
      } catch (InterruptedException e){}
      if(runFlag)
        t.setText(Integer.toString(count++));
    }
  }
  class StartL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      if(selfThread == null) {
        selfThread = new Thread(Counter3.this);
        selfThread.start();
      }
    }
  }
  class OnOffL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      runFlag = !runFlag;
    }
  }
  public static void main(String[] args) {
    Counter3 applet = new Counter3();
    Frame aFrame = new Frame("Counter3");
    aFrame.addWindowListener(
      new WindowAdapter() {
        public void windowClosing(WindowEvent e) {
          System.exit(0);
        }
      });
    aFrame.add(applet, BorderLayout.CENTER);
    aFrame.setSize(300,200);
    applet.init();
    applet.start();
    aFrame.setVisible(true);
  }
} ///:~ 

Now


the


run( )

is inside the class, but it’s still dormant after


init( )

completes. When you press the


start

button, the thread is created (if it doesn’t already exist) in the


somewhat obscure expression:

new
Thread(Counter3.this);

When


something has a

Runnable
interface, it simply means that it has a
run( )
method, but there’s nothing special about that – it doesn’t
produce any innate threading abilities, like those of a class inherited from
Thread.
So to produce a thread from a
Runnable
object, you must create a thread separately and hand it the
Runnable
object; there’s a special constructor for this that takes a
Runnable
as its argument. You can then call
start( )
for that thread:
selfThread.start();

This


performs the usual initialization and then calls


run( )

.

The


convenient aspect about the


Runnable
interface

is that everything belongs to the same class. If you need to access something,


you simply do it without going through a separate object. The penalty for this


convenience is strict, though – you can have only a single thread running


for that particular object (although you can create more objects of that type,


or create other threads in different classes).

Note


that the


Runnable

interface is not what imposes this restriction. It’s the combination of


Runnable

and your main class that does it, since you can have only one object of your


main class per application.


Making
many threads

Consider


the creation of many different threads. You can’t do this with the


previous example, so you must go back to having separate classes inherited from


Thread

to encapsulate the


run( )

.


But this is a more general solution and easier to understand, so while the


previous example shows a coding style you’ll often see, I can’t


recommend it for most cases because it’s just a little bit more confusing


and less flexible.

The


following example repeats the form of the examples above with counters and


toggle buttons. But now all the information for a particular counter, including


the button and text field, is inside its own object that is inherited from


Thread

.


All the fields in


Ticker

are


private

,


which means that the


Ticker

implementation can be changed at will, including the quantity and type of data


components to acquire and display information. When a


Ticker

object is created, the constructor requires a handle to an AWT


Container,

which


Ticker

fills


with its visual components. This way, if you change the visual components, the


code that uses


Ticker

doesn’t need to be modified.

//: Counter4.java
// If you separate your thread from the main
// class, you can have as many threads as you
// want.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
 
class Ticker extends Thread {
  private Button b = new Button("Toggle");
  private TextField t = new TextField(10);
  private int count = 0;
  private boolean runFlag = true;
  public Ticker(Container c) {
    b.addActionListener(new ToggleL());
    Panel p = new Panel();
    p.add(t);
    p.add(b);
    c.add(p);
  }
  class ToggleL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      runFlag = !runFlag;
    }
  }
  public void run() {
    while (true) {
      if(runFlag)
        t.setText(Integer.toString(count++));
       try {
        sleep(100);
      } catch (InterruptedException e){}
    }
  }
}
 
public class Counter4 extends Applet {
  private Button start = new Button("Start");
  private boolean started = false;
  private Ticker[] s;
  private boolean isApplet = true;
  private int size;
  public void init() {
    // Get parameter "size" from Web page:
    if(isApplet)
      size =
        Integer.parseInt(getParameter("size"));
    s = new Ticker[size];
    for(int i = 0; i < s.length; i++)
      s[i] = new Ticker(this);
    start.addActionListener(new StartL());
    add(start);
  }
  class StartL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      if(!started) {
        started = true;
        for(int i = 0; i < s.length; i++)
          s[i].start();
      }
    }
  }
  public static void main(String[] args) {
    Counter4 applet = new Counter4();
    // This isn't an applet, so set the flag and
    // produce the parameter values from args:
    applet.isApplet = false;
    applet.size =
      (args.length == 0 ? 5 :
        Integer.parseInt(args[0]));
    Frame aFrame = new Frame("Counter4");
    aFrame.addWindowListener(
      new WindowAdapter() {
        public void windowClosing(WindowEvent e) {
          System.exit(0);
        }
      });
    aFrame.add(applet, BorderLayout.CENTER);
    aFrame.setSize(200, applet.size * 50);
    applet.init();
    applet.start();
    aFrame.setVisible(true);
  }
} ///:~ 
Ticker

contains not only its threading equipment but also the way to control and


display the thread. You can create as many threads as you want without


explicitly creating the windowing components.

In


Counter4

there’s an array of


Ticker

objects called


s

.


For maximum flexibility, the size of this array is initialized by reaching out


into the Web page using applet parameters. Here’s what the size parameter


looks like on the page, embedded inside the applet description:

<applet code=Counter4 width=600 height=600>
<param name=size value="20">
</applet>

The


param,
name,
and
value
are all Web-page keywords.
name
is what you’ll be referring to in your program, and
value
can be any string, not just something that resolves to a number.

You’ll


notice that the determination of the size of the array


s

is done inside


init( )

,


and not as part of an inline definition of


s

.


That is, you


cannot

say as part of the class definition (outside of any methods):

int size = Integer.parseInt(getParameter("size"));
Ticker[] s = new Ticker[size];

You


can compile this, but you’ll get a strange null-pointer exception at run


time. It works fine if you move the


getParameter( )

initialization


inside of


init( )

.


The

applet
framework performs the necessary startup to grab the parameters before entering
init( ).

In


addition, this code is set up to be either an

applet
or an
application.
When it’s an application the
size
argument is extracted from the command line (or a default value is provided).

Once


the size of the array is established, new


Ticker

objects are created; as part of the


Ticker

constructor the button and text field for each


Ticker

is


added to the applet.

Pressing


the


start

button means looping through the entire array of


Ticker

s


and calling


start( )

for each one. Remember,


start( )

performs necessary thread initialization and then calls


run( )

for that thread.

The


ToggleL

listener


simply inverts the flag in


Ticker

and when the associated thread next takes note it can react accordingly.

One


value of this example is that it allows you to easily create large sets of


independent subtasks and to monitor their behavior. In this case, you’ll


see that as the number of subtasks gets larger, your machine will probably show


more divergence in the displayed numbers because of the way that the threads


are served.

You


can also experiment to discover how important the


sleep(100)

is inside


Ticker.run( )

.


If you remove the


sleep( )

,


things will work fine until you press a toggle button. Then that particular


thread has a false


runFlag

and the


run( )

is just tied up in a tight infinite loop, which appears difficult to break


during multithreading, so the responsiveness and speed of the program really


bogs down.


Advertisement

Daemon
threads

A


“daemon” thread is one that is supposed to provide a general


service in the background as long as the program is running, but is not part of


the essence of the program. Thus, when all of the non-daemon threads complete


the program is terminated. Conversely, if there are any non-daemon threads


still running the program doesn’t terminate. (There is, for instance, a


thread that runs


main( ).

)

You


can find out if a thread is a daemon by calling

isDaemon( ),
and you can turn the daemonhood of a thread on and off with
setDaemon( ).
If a thread is a daemon, then any threads it creates will automatically be
daemons.

The


following example demonstrates daemon threads:

//: Daemons.java
// Daemonic behavior
import java.io.*;
 
class Daemon extends Thread {
  private static final int SIZE = 10;
  private Thread[] t = new Thread[SIZE];
  public Daemon() {
    setDaemon(true);
    start();
  }
  public void run() {
    for(int i = 0; i < SIZE; i++)
      t[i] = new DaemonSpawn(i);
    for(int i = 0; i < SIZE; i++)
      System.out.println(
        "t[" + i + "].isDaemon() = "
        + t[i].isDaemon());
    while(true)
      yield();
  }
}
 
class DaemonSpawn extends Thread {
  public DaemonSpawn(int i) {
    System.out.println(
      "DaemonSpawn " + i + " started");
    start();
  }
  public void run() {
    while(true)
      yield();
  }
}
 
public class Daemons {
  public static void main(String[] args) {
    Thread d = new Daemon();
    System.out.println(
      "d.isDaemon() = " + d.isDaemon());
    // Allow the daemon threads to finish
    // their startup processes:
    BufferedReader stdin =
      new BufferedReader(
        new InputStreamReader(System.in));
    System.out.println("Waiting for CR");
    try {
      stdin.readLine();
    } catch(IOException e) {}
  }
} ///:~ 

The


Daemon

thread sets its daemon flag to “true” and then spawns a bunch of


other threads to show that they are also daemons. Then it goes into an infinite


loop that calls


yield( )

to give up control to the other processes. In an earlier version of this


program, the infinite loops would increment


int

counters,


but this seemed to bring the whole program to a stop. Using


yield( )

makes the program quite peppy.

There’s


nothing to keep the program from terminating once


main( )

finishes its job since there are nothing but daemon threads running. So that


you can see the results of starting all the daemon threads,


System.in

is set up to read so the program waits for a carriage return before


terminating. Without this you see only some of the results from the creation of


the daemon threads. (Try replacing the


readLine( )

code with


sleep( )

calls of various lengths to see this behavior.)


Contents

|

Prev

|

Next
CodeGuru Logo

CodeGuru covers topics related to Microsoft-related software development, mobile development, database management, and web application programming. In addition to tutorials and how-tos that teach programmers how to code in Microsoft-related languages and frameworks like C# and .Net, we also publish articles on software development tools, the latest in developer news, and advice for project managers. Cloud services such as Microsoft Azure and database options including SQL Server and MSSQL are also frequently covered.

Property of TechnologyAdvice. © 2026 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.