You are on page 1of 49

Topics

1. Exceptions
2. Threads 
3. I/O 
4. Introduction to Java® GUI and Swing 
1. Exceptions
Java® provides ways to manage exceptions, i.e. errors that may occur during the execution of a
program that disrupt the normal flow of instructions. When an unexpected error condition
happens during runtime a Java program "throws" an exception. This can avoid the premature
termination of the program, which in some cases is not necessary. A function that detects an
error throws an exception, which can be caught by an exception handler. The exception object
contains information about the exception, e.g. its type and the state of the program when the
error had occurred.
The runtime system tries to find some code to handle the error, i.e. an exception handler. The
exception causes a search through the call stack to find an appropriate handler to handle the
exception. The Java® runtime system searches backwards through the call stack to find any
methods that are interested in handling a particular exception. The corresponding, or the default,
exception handler catches the exception and may throw a new exception or attempt to handle the
exception. If the runtime system, after an exhaustive search of all the methods on the call stack,
cannot find an encompassing exception handler the exception is caught by a default exception
handler. The latter, usually, prints information about the thrown exception, e.g. where it was
thrown, and, then, the runtime system, and, consequently, the Java® program, terminate. An
exception handler can catch an exception based on its group or general type by specifying any of
the exception's superclasses in the catch statement.
An exception in Java® is an object which is a subclass of the class Throwable, and, in most
cases, it is derived by the class Exception, which is a subclass
of Throwable. The Throwable  class is the superclass of all errors and exceptions in the Java®
language. Only objects that are instances of this class, or of one of its subclasses, can
be thrown by the Java® VM, or by a Java® throw statement.
In Java®, exceptions are managed by first trying something in a try{....} compound block. The
code that might throw an exception is placed in a try block. If an error occurs in that block an
exception is thrown. Then, acatch{.....} block is used to catch any exception that is thrown. Zero
or more catch blocks may follow the try block. The code that may provide the action that must
be taken in the case of a certain exception occurs is provided in acatch block that follows
the try block. Each catch block specifies the type of exception it can catch. Only
the Throwable class or one of its subclasses can be the argument type in a catch clause. An
optional finally {....}block, which follows the last catch block or the try block when there are
no catch blocks, provides code that is executed in any case, i.e. regardless whether an exception
occurs or not.
Example
class Exceptions1
{
       public static void main(String args[])
       {
            double d[] = new double[4];
            for(int j=0 ; j<d.length ; j++)
                d[j] = j*7.15 + 2.19 ;
            for(int j=0 ; j<=d.length ; j++)
                {
                   try{
                          System.out.print( "  d[" + j + "] / " + j + " = ");
                          if(j==0)
                          & #160;  throw new DivideByZeroException();
                          System.out.println(d[j]/j);
                       }
                   catch(ArrayIndexOutOfBoundsException e)
                       {
                          System.out.println("Exception: " + e.getMessage());
                          System.out.print("e.printStackTrace(): ");
                          e.printStackTrace();
                       }
                   catch(DivideByZeroException e)
                       {
                          System.out.println("Exception: " + e.getMessage());
                       }
                   finally
                       {
                          System.out.println("Inside finally()\n");
                       }
                  }
             System.out.println( "\n  Program exiting \n");
       }
}
class DivideByZeroException extends ArithmeticException
{
     DivideByZeroException()
     {
       super("Trying to divide by zero");
     }
}
 
Output:
  d[0] / 0 = Exception: Trying to divide by zero
Inside finally()
  d[1] / 1 = 9.34
Inside finally()
  d[2] / 2 = 8.245000000000001
Inside finally()
  d[3] / 3 = 7.880000000000002
Inside finally()
  d[4] / 4 = Exception: null
e.printStackTrace(): java.lang.ArrayIndexOutOfBoundsException
          at Exceptions1.main(Compiled Code)
Inside finally()
  Program exiting
 
A checked exception requires the specification of the way that the exception should be handled.
If a function may result in a checked exception, this must be defined using the keyword throws at
the definition of the class, after its name. A method can only throw checked exceptions that have
been specified in its declaration. Java® requires that a method should either catch, or specify all
checked exceptions that can be thrown within the scope of that method.
Unchecked exceptions are of type RuntimeException, Error, or subclasses of them, and can be
thrown anywhere without the need to specify the possibility of such exceptions to be thrown.
Classes Exception and Error are subclasses of the class Throwable, as shown in the following
figure, which was adapted from Sun's Java® Tutorial. Class RuntimeException is a subclass of
the class Exception.

Any method (i.e. function) that may produce a non-RuntimeException should declare the type of
exception that it can produce using the throws keyword, or that exception should be caught in
a try/catch block in the method. The basic error type is class Exception, although there are more
specific types of exceptions. When a checked exception occurs, the throw keyword may used to
actually create the Exception object and either an exception handler should catch and handle the
exception, or the function exits. Java® uses the "termination model of exception", since control
cannot return to the throw point, when an exception is thrown.
Code that may produce an exception is placed within the try block of a try-catch statement. If the
code within thetry block fails, the code within a corresponding catch block, if exists, is executed.
When an exception is thrown the control goes out of the try block and searches the catch blocks
to find an appropriate handler. If the code succeeds, then control passes to the next statement
following the try-catch statement or to the finally block. In the presence of a finally block, the
latter is also executed irrespectively of whether an exception has occurred. If no exception
handler is found for an exception, the search continues in the enclosing try block and a Java®
application terminates if no exception handler is eventually found.
Class Error  is, also, provided in Java® to be used for serious problems that should not be
caught. The Error class is a subclass of the Throwable class. RuntimeExceptions should be dealt
directly rather than throw an exception.

The main advantages of using exception handling are that regular code is separated from error
handling code making the code much more readable. Also, error types can be grouped together,
and the errors can propagate up the call stack to find a proper exception handler.

However, use of exception handling for purposes other than error handling should be avoided
since it can cause a performance overhead. Exception handling should be used only when a
method is unable to detect and process an error and, thus, it throws an exception under an
exceptional situation, instead of processing it locally. You should avoid using exception-
handling for purposes other than error handling. The latter reduces clarity but also results in an
execution time overhead imposed by the exception handling code. However, when an exception
does not occur there is almost no execution overhead.

An exception handler can do several things such as try to recover and resume execution, rethrow
an exception, throw a different type of exception. As soon as the exception handler begins
execution the try block has expired, and control is in a different scope. If an exception occurs in a
handler, it may be processed only by code outside the try block in which the original exception
had been thrown. Similarly, a rethrown exception can be caught only by an exception handler
after the next enclosing try block.
A subclass class overridden method can have in its throws list only the same with, or a subset of,
its superclass's method throws list.
2. Threads
A threat is a single sequence of steps executed one at a time, i.e. a sequential flow of control,
running within a program and taking advantage of the resources allocated for that program and
its environment.
Multithreading allows to a program to perform several tasks, i.e. to use more than one flow of
control, concurrently. All threads of a multithreading Java® program share the same data and
system resources, running at the same time and performing different tasks. Since most computers
have only one CPU, threads share the CPU with other threads.

Threats can be used by one of the following ways:

By providing a subclass of the Thread class


By providing a class that implements the Runnable interface.

 1. Providing a subclass of the Thread class:


We need to provide a subclass of the Thread class. This subclass should override
the run() method of classThread. The latter must contain all code that is to be executed within
the thread. An instance of the subclass can then be allocated and started.
class MyThreadClass extends Thread
{
     public void run()
     {
          // Code to be executed within the thread
     }
}
 
Then, we can create a new thread by instantiating a subclass of the Thread class. Then, we can
configure the thread, e.g. by setting its initial priority, name, etc. Finally, we can run it, by
calling the start() method that is inherited from the class Thread, which invokes the new
thread's run() method.
MyThreadClass x = new MyThreadClass();
x.start();

However, using this approach is not possible when we need to extend some other class, since
Java® does not allow multiple inheritance.

Example
class MyThreadClass extends Thread
{
     String name;
     MyThreadClass(String str)
       {
            name = str;
       }
     public void run()
     {
            for(int i=0; i<5;i++)
                {
                    System.out.println("Thread: " + name);
                    try
                       {
                          Thread.sleep((int) (Math.random()*1000));
                       }
                    catch(InterruptedException e)
                       {
                          System.out.println("Catch: " + getName());
                       }
                }
     }
}
class ThreadExample1
{
     public static void main(String args[])
       {
             MyThreadClass  th1 = new MyThreadClass("Thread-1");
             MyThreadClass  th2 = new MyThreadClass("Thread-2");
             MyThreadClass  th3 = new MyThreadClass("Thread-3");
 
             th3.start();
             th1.start();
             th2.start();
       }
}
Output:    > java ThreadExample1
Thread: Thread-3
Thread: Thread-1
Thread: Thread-2
Thread: Thread-1
Thread: Thread-1
Thread: Thread-3
Thread: Thread-1
Thread: Thread-2
Thread: Thread-3
Thread: Thread-1
Thread: Thread-3
Thread: Thread-2
Thread: Thread-3
Thread: Thread-2
Thread: Thread-2

2. Implementing the Runnable Interface


The other way to create a thread is to declare a class that implements the Runnable interface.
The Runnableinterface requires the implementation of the run() method, in which all code that is
to be executed within the thread should be placed. An instance of the class can then be allocated.
Finally, a Thread object can be created passing as an argument the object of the class that
implements the Runnable interface, and, then start the thread.
class MyRunnableClass implements Runnable
{
     public void run()
     {
          //  Code to be executed within the thread
     }
}
A new thread can by created by creating a Thread object using an object of
type MyRunnableClass, and, then, by calling the start() method, of the Threat class, which runs
the thread.
MyRunnableClass x = new MyRunnableClass();
Thread t = new Thread(x);
t.start();
Example
class MyRunnableClass implements Runnable
{
     String name;
     MyRunnableClass(String str)
       {
            name = str;
       }
     public void run()
     {
            for(int i=0; i<5;i++)
                {
                    System.out.println("Thread: " + name);
                    try
                       {
                          Thread.sleep((int) (Math.random()*1000));
                       }
                    catch(InterruptedException e)
                       {
                          System.out.println("InterruptedException in " + name + " thread.");
                       }
                }
     }
}
class ThreadExample2
{
     public static void main(String args[])
       {
             MyRunnableClass  r1 = new MyRunnableClass("Thread-1");
             MyRunnableClass  r2 = new MyRunnableClass("Thread-2");
             MyRunnableClass  r3 = new MyRunnableClass("Thread-3");
             Thread th1 = new Thread(r1);
             Thread th2 = new Thread(r2);
             Thread th3 = new Thread(r3);
             th3.start();
             th1.start();
             th2.start();
       }
}
 
Output:      > java ThreadExample2
Thread: Thread-3
Thread: Thread-1
Thread: Thread-2
Thread: Thread-2
Thread: Thread-2
Thread: Thread-1
Thread: Thread-3
Thread: Thread-2
Thread: Thread-3
Thread: Thread-2
Thread: Thread-1
Thread: Thread-1
Thread: Thread-3
Thread: Thread-3
Thread: Thread-1

States of a thread during its lifetime:

New: A new thread is one that has been created, i.e. using the new operator, but has not yet been
started.
Runnable (ready state): A thread becomes runnable once its start() method has been invoked,
which means that the code in the run() method can execute whenever the thread receives CPU
time from the operating system. A threat that had been set to sleep and the time interval has
expired, a thread which was waiting and has been notified, a thread that had been suspended and
it has been resumed, and a thread that had been blocked by an I/O request and the I/O has been
completed are also in a runnable state.
Not Runnable (blocked state): A thread can become not runnable if: the thread's sleep() method
is invoked (in which case, the thread remains blocked for a specified number of milliseconds,
giving a chance to lower-priority threads to run); the thread's suspend() method is invoked (in
which case, the thread remains blocked until itsresume() method is invoked); the thread calls
the wait() method of an object (in which case, the thread remains blocked until either the
object's notify() method or its notifyAll() method is called from another thread); the thread has
blocked on an I/O operation (in which case the thread remains blocked until the I/O operation
has been completed)
Dead: A thread can die either because the run() method has finished executing, i.e. has
terminated, or because the thread's stop() method has been called. The latter should be avoided
because it throws a ThreadDeath which is a subclass of Error.

The following diagram shows the possible life cycle of a thread:


Note that the methods stop(), suspend(), and resume() have been deprecated, and, therefore,
should be avoided to manage threads. Instead of using the stop() method, someone can modify
some variable to indicate that the target thread should stop running. The target thread should
check this variable regularly, and return from its run method as soon as this variable indicates
that it is to stop running. A variable indicating the desired state of the thread can be used to
specify whether it should be active or suspended, in order to avoid the suspend(),
andresume() methods. When the desired state is suspended, the thread can wait using
the wait() method. When the thread is resumed, the target thread is notified using
the notify() method.
The wait() method makes a thread wait until some condition is satisfied. Actually wait() not only
pauses the corresponding tread but also releases the lock on the object, which allows other
threads to invoke synchronized code on that object. The notification
methods notify() and notifyAll() can be used to inform waiting threads that there was some
change that might have caused the satisfaction of that condition. Almost always
the notifyAll() method is used to wake up all waiting threads, while notify() picks one of the
waiting threads and wakes it up. Notifications affect only threads that have been waiting ahead of
the notification, i.e. the wait() has been executed before the occurrence of the notification. The
wait and notify methods can be invoked only fromsynchronized code, either directly or indirectly
(through another method called from the synchronized code).
Typically wait() is used as follows. Note that the condition test should always be in a loop.
synchronized  returnType functionName()
 {
          while(!condition)
                     wait();
            statements to be executed when the condition is true
          notifyAll();
 }

The normal and cleanest way to end a thread's life is having the run() method return.
Method interrupt() can be used to interrupt a thread execution.
Every thread has a priority, which is a number
between Thread.MIN_Priority and Thread.MAX_Priority. Threads with higher priority are
executed in preference to threads with lower priority. When code running in some thread creates
a new Thread object, the new thread has its priority initially set equal to the priority of the
creating thread.

When multiple threads are runnable (i.e. ready to be executed), the runtime system chooses the
runnable thread with the highest priority for execution. Only when that thread stops, yields, or
becomes not runnable for some reason a lower priority thread will start executing.

Java® platforms that support time-slicing allow each thread of equal priority to run by providing
to all threads a limited amount of the processor's time. On Java® platforms that do not support
time-slicing a thread of a given priority runs to completion or until a higher (but not equal)
priority thread becomes runnable. Method yield(), which is useful only on non-time-slicing
systems, allows threads of the same priority to run.
Execution of multiple threads in some order is called scheduling. This is supported by  a very
simple, deterministic scheduling algorithm (fixed priority scheduling) of the Java® runtime. This
algorithm schedules threads based on their priority relative to other runnable threads. The Java®
scheduler keeps the highest priority thread running at all times, and when time-slicing is
supported allows equal high-priority threads to execute by giving slices of the processor time to
them in sequence. In general, it is good practice to have the continuously running part of a
program, or  threads that do frequent and continual updates set to MIN_PRIORITY to avoid
blocking other threads.

Threads on multiprocessor machines utilize the multiple processors. A number of the highest-
priority threads equal to the number of the available processors execute.

Someone can set and get the name of a threat using the functions setName() and getName(),
respectively, of the Threat class. The priority can be set and got using the methods set and get
methods of the Thread class.
The thread object of the currently running thread can be obtained using
the currentThread() method of the Thread class.
Example
class MyRunnableClass implements Runnable
{
     String name;
     MyRunnableClass(String str)
       {
            name = str;
       }
     public void run()
     {
            for(int i=0; i<5;i++)
                {
                    System.out.println("Thread: " + name);
                    try
                       {
                          Thread.sleep((int) (Math.random()*3));
                       }
                    catch(InterruptedException e)
                       {
                          System.out.println("InterruptedException in " + name + " thread.");
                       }
                }
     }
}
class ThreadExample3
{
     public static void main(String args[])
       {
             MyRunnableClass  r1 = new MyRunnableClass("Thread-1");
             MyRunnableClass  r2 = new MyRunnableClass("Thread-2");
             MyRunnableClass  r3 = new MyRunnableClass("Thread-3");
             Thread th1 = new Thread(r1);
             Thread th2 = new Thread(r2);
             Thread th3 = new Thread(r3);
             th2.setPriority(10);
             th3.setPriority(1);
             System.out.println(" th1 priority: " + th1.getPriority());
             System.out.println(" th2 priority: " + th2.getPriority());
             System.out.println(" th3 priority: " + th3.getPriority() + "\n");
             th3.start();
             th1.start();
             th2.start();
       }
}
Output:     > java ThreadExample3
  th1 priority: 5
  th2 priority: 10
  th3 priority: 1
Thread: Thread-2
Thread: Thread-2
Thread: Thread-2
Thread: Thread-2
Thread: Thread-2
Thread: Thread-1
Thread: Thread-1
Thread: Thread-1
Thread: Thread-3
Thread: Thread-1
Thread: Thread-1
Thread: Thread-3
Thread: Thread-3
Thread: Thread-3
Thread: Thread-3

Java®'s garbage collector runs as a low-priority thread when processor time is available and
when there are no higher-priority runnable threads.

Each thread may or may not be marked as a daemon. A daemon thread is a thread that runs in the
background (when the processor is available) for the benefit of other threads. An example of a
daemon thread is the garbage collector. When code running in some thread creates a
new Thread object, the new thread is a daemon thread if and only if the creating thread is
a daemon. Daemon threads do not prevent a program from terminating, since the program exits
when only daemon threads remain in it. The Java® VM exits when the only threads running are
all daemon threads. The function setDaemon(boolean on) can be used, before the thread is
started, to mark a thread as a daemon thread or a user thread. If the argument to
the setDaemon(boolean on) is true, the thread is marked as a daemon thread.
A selfish thread is a thread that never voluntarily gives up the control of the CPU, continuing
running until either the thread's run() method terminates, or until the thread is preempted by a
higher priority thread. Time-slicing systems do not allow to a selfish thread to keep the control
when there are other multiple runnable threads of equal priority, allowing the other threads to
run.

In particular, someone should never rely on time-sharing and should write well behaved threads
that periodically give up, voluntarily, the control of the CPU, giving to other threads an
opportunity to run.

Race hazard, or race condition, is the situation in which two or more threads can modify the
same data in a way that the data can be corrupted by inconsistent changes.
Starvation is an indefinite postponement of the execution of lower-priority threads due to higher-
priority threads which do not give them the chance to run. Starvation occurs when one (or more
threads) in a program cannot progress because it is blocked from gaining access to a certain
resource.
Deadlock is the worst case of starvation, which occurs when two or more threads are waiting on
a condition that cannot be satisfied, e.g. when two (or more) threads are each waiting for the
action of the other(s). Race conditions can arise from multiple, asynchronously executing threads
that are trying to access a single object (data) at the same time which may result on a wrong
result and inconsistencies. Synchronization can be used to avoid such race problems.
When several concurrent threads are competing for resources, starvation and deadlock should be
prevented. Each thread should get enough access to limited resource in order to reasonably
progress without causing problems to the other threads or corrupting common data.
Synchronization
In some cases separate, concurrently running threads share data in a way that they must take into
account the state of other threads. An example of such a case is the so-
called producer/consumer scenario in which the "producer" thread generates data that are
consumed by a "consumer" thread.
In such cases, problems can be avoided using synchronization, which allows a thread to lock an
object and in case of another thread trying to invoke a synchronized method on the same object,
the second thread will be blocked until the object is unlocked from the first thread. A lock is
associated with every object that hassynchronized code.
Critical code sections are code segments (e.g. a method) within a program that access the same
data (e.g. object) from separate but concurrent threads. In the Java® language,
the synchronized keyword is used to identify a critical section. Whenever a synchronized method
of a class is invoked, the associated object is locked by that method. A synchronized method
cannot be called on the same object until the object is unlocked, i.e. another thread cannot invoke
a synchronized method on that object until the lock is released. When a synchronized method is
invoked on an object it obtains its lock, not allowing any other thread to invoke any
synchronized method until it releases the lock. A thread can voluntarily call wait(), which
releases the lock and the processor, and wait in a queue while other threads are able to obtain the
lock and invoke synchronized code. The methods notify() and notifyAll()  can be used to signal to
waiting threads to become ready again and attempt to obtain the lock. If waiting threads are not
notified the threads will wait forever causing a deadlock.
Java® locks are reentrant, i.e. it is allowed to a thread to re-acquire a lock that it already holds.
This avoids problems in which arise when a thread calls from a synchronized method of an
object another synchronized method of the same object, which could lead into deadlock.
Static methods can also be synchronized. In that case a "class lock" for the corresponding class is
used, which does not allow two threads to execute synchronized static methods on the same class
at the same time.
A synchronized statement is an alternative, in some cases, way to execute synchronized code that
locks the object so that no synchronized method can be invoked on that object, since it is locked.
 
      synchronized(objectTobeLocked)
         {
               statements
         }

Thread Group
In Java® every Thread object is a member of a thread group, which allows to handle multiple
threads, which belong in the same thread group, as a group. The ThreadGroup class is used to
implement thread groups and allow to deal with the threads in a thread group as a group.
The Java® runtime system creates a ThreadGroup by default, named main, as soon as a Java®
application starts. A newly created thread is placed in the same group with the thread group in
which the thread that created it belongs. Therefore, unless specified otherwise, all new threads
that are created become members of the main thread group by default. In an applet a newly
created thread may belong to some other than main thread group depending on the browser. A
thread cannot change thread group after its creation.
A ThreadGroup  can contain any number of threads, which usually, however, are related in some
way, and even other ThreadGroup objects. The top-most ThreadGroup in a Java® application is
the ThreadGroup named main, which is the default thread group.
Method activeCount() gives the number of active threads in a thread group plus those in all its
child thread groups. The ThreadGroup class provides a variety of methods to manage, and
operate ThreadGroup objects and the Thread objects that are members of those thread groups.
 
  3. I/O
The System class, which is a final class and has private constructors (i.e. not allowing its
extension), provides several useful class variables and methods. For example System.out is
a PrintStream object that implements the standard output stream, and System.getProperty(String
key) is a static method that returns the system property indicated by the specified key.
The System class provides:
 in: the standard input stream to read input data (static InputStream object)
 out: the standard output stream to output results (static PrintStream object)
 err: the standard error stream to display error messages (static PrintStream object)
Both standard output and error, which are derived from the PrintStream class, can invoke one of
the PrintStream's methods print(), println(), and write() to print text to the stream. The latter
method, which is not that common is used to write non-ASCII data, i.e. to write bytes to the
stream.
The java.io package provides a collection of stream classes that support reading from and writing
to an external destination. The provided classes operate on either characters or byte data types.

The abstract superclasses for character streams in java.io are the Reader (abstract class for
reading character streams) and the Writer (abstract class for writing to character streams) classes,
which partially provide the functionality for the characters reader and writer stream classes,
respectively. [The following inserted figures have been adapted from the on-line Java®
Tutorial of SUN]
In general, readers and writers should be preferred to read and write information since they can
handle any character in the Unicode character set because they use 16-bits per character.

Similarly, the abstract superclasses for byte streams in java.io are the InputStream (abstract class
for reading byte streams) and the OutputStream (abstract class for writing to byte streams)
classes.
Reader and InputStream provide methods for reading characters and bytes, respectively. They
also provide methods for marking a location in the stream, skipping input, and resetting the
current position. Similarly, Writer and OutputStream provide methods for writing characters and
bytes, respectively.
All readers, input streams, writers, and output streams are automatically opened upon the
creation of the corresponding objects. Although they are eligible to be closed by the garbage
collector, as soon as they are not referenced in any way, they can be explicitly closed by calling
their close() method.

The above stream classes can be categorized into two groups: one that deals with reading and
writing data, and another that performs some kind of operation on the data while reading and
writing. The former are the ones shown with gray color in the above figures, while the latter are
the ones shown in white.

A useful class provided by the java.io package is the StreamTokenizer class.


A StreamTokenizer object can be created using as argument to its constructor call
an InputStreamReader object. Then, the StreamTokenizer object may be used on an input stream
to parses it into tokens, which it reads one at a time.
  4. Introduction to Java® GUI and Swing
Java® Foundation Classes(JFC) provide features to facilitate the development of Graphical
User Interfaces (GUIs). Among other, JFC provides Swing, which includes
several components that can be used for the development of GUIs, support for a choice on the
look and feel that a program uses, and provides Java®2D for high quality 2D graphics. Finally,
swing allows the specification which look and feel a program should use. This is done with
the setLookAndFeel() method of the UIManager class. Swing is built on top of
the AWT (Abstract Window Toolkit) using the AWT infrastructure. However, Swing provides its
own graphical user interface (GUI) components, many of which have a close relation or
correspondence to the AWT components. Essentially, Swing is an extension and improvement to
the AWT.
JFC 1.1 (with Swing API 1.1) is built into JDK 1.2. The latest JFC development is the
JFC/Swing portion of JDK 1.3, which is code-named Kestrel and it was released this year.

The Swing API is provided in the following Swing packages:

  javax.swing   (the main swing package)


  javax.swing.border
   javax.swing.colorchooser
   javax.swing.event
   javax.swing.filechooser
   javax.swing.plaf
   javax.swing.plaf.basic
   javax.swing.plaf.metal
   javax.swing.plaf.multi
   javax.swing.table
   javax.swing.text
   javax.swing.text.html
   javax.swing.text.html.parser
   javax.swing.text.rtf
   javax.swing.tree
   javax.swing.undo
The AWT (Abstract Window Toolkit) was first used to develop GUI's in Java®, and provided the
foundation on which the JFC and swing were built. The main difference
between AWT and Swing is that the components of the latter are implemented without any native
code. Because of this, swing components are called lightweight, while AWT components, which
use native code, are called heavyweight components. Lightweight components are drawn entirely
using Java®, while heavyweight components use native peers. Actually, AWT 1.1 also
introduced some lightweight components, while earlier versions were based solely on
heavyweight components. Swing components, in general, have much more capabilities than the
AWT corresponding ones. For instance some Swing components (such as labels and buttons) can
display icons, while the corresponding AWT components cannot. Swing provides a number of
additional components that were not provided by AWT.
Swing provides several standard components (e.g. buttons, checkbuttons, radiobuttons, menus,
lists, labels, and text areas) to create a program's GUI using one or more containers (e.g. frames,
dialogs, windows and tool bars). Most Swing components that begin with J, except the top-level
containers, are subclasses of the JComponent class. The letter J is used to differentiate the actual
extra user interface classes provided by Swing from the support classes that it provides. Swing
components inherit many features from the JComponent class, such as a configurable look and
feel, borders, and tool tips, as well as many methods. In addition, some Swing components can
display images on them. The JComponent class extends the Container class (provides support for
adding and laying out components), which itself extends the Component class (provides support
for  painting, events, layout etc.). JComponent is the base class for almost all lightweight J
components. Therefore, all swing lightweight components (derived from the JComponent class)
are subclasses of the Container class of AWT.
The following article, by Bill Harlan, provides some useful information about Improving Swing
Performance.

Containers
Every program with a Swing GUI contains at least one top-level Swing container, i.e., in general,
an instance of a JApplet, JFrame, or JDialog, which enables the painting and event handling of
the Swing components. The JApplet, JFrame, or JDialog, are considered top-level containers
because they are used to provide an area in which the other containers and components can
appear. Other containers, such as the JPanel, are used to facilitate the positioning and sizing of
other containers and components. Between a top-level container and an intermediate container, a
content pane (JRootPane), which is also an intermediate container, is indirectly provided. The
content pane contains all of visible components of its GUI, except a menu bar. One of
the add()methods of a container can be used to add a component to it. The component to be
added is used as argument to the add method. In some cases another argument, which has to do
with the layout, is used with the add()method.
Besides the top-level containers (JApplet, JFrame, JDialog, and JWindow) there are special-
purpose containers (such as the JInternalFrame, JLayeredPane, and JRootPane) that are used
for special purposes, and general-purposecontainers (such as JPanel, JScroll
Pane, JSplitPane, JTabbedPane, and JToolBar) that are used for other any general purpose.
The JApplet and JFrame classes should be used to implement Swing applets or applications,
respectively. BothJApplet and JFrame classes are containers that contain an instance of
the JRootPane class. The latter contains the content pane, which is a container, that contains all
the components contained in the applet or application. Therefore, components should be added
and layout managers should be set to the content pane.

Atomic Components
There are many atomic components (i.e. components that exist as self-sufficient entities and not
to hold other Swing components). Atomic components can be grouped into 3 categories:

 Basic Controls, which are components that can primarily be used to get input from the
user (such as JSlider, JTextField,  JButton,  JComboBox,  JList, and JMenu)
 Uneditable Information Displays, which are used to display information to the user (such
as JProgressBar, JLabel,and JToolTip)
 Editable Displays of Formatted Information, which are components that can be used to
display formatted information that can be editable by the user (such
as JColorChooser,  JTextComponent,  JFileChooser,  JTable, and JTree)

Applets and Applications


Swing applets use the JApplet class which is a subclass of the AWT  Applet class and implements
the Accessible and RootPaneContainer interfaces. The following simple example shows an
applet with a single component a JLabel. The BorderLayout manager is used by the JApplet for
its content pane (which is used in both Swing applets and applications), in contrast to
the FlowLayout manager used by the Applet class.
 

SwingApplet1.java

import java.awt.*;
import javax.swing.*;

public class SwingApplet1 extends JApplet


{
    public void init()
      {
       Icon icon = new ImageIcon("1.124.gif", "1.124 GIF");
       JLabel label = new JLabel(" Test", icon, SwingConstants.CENTER);
       getContentPane().add(label);
   }
}
The above applet can be executed using an html file like the following:

SwingApplet1.html

<html>
<head>
    <title>JApplet Example # 1</title>
</head>
<body>

<center>
<h2>
<u>JApplet Example # 1</u></h2>
<hr><applet code="SwingApplet1.class" WIDTH="486" HEIGHT="94"></applet>
<hr></center>
<p><br>
</body>
</html>
Then, appletviewer (using the command: appletviewer SwingApplet1.html) displays the
following window:

Similarly, the JFrame class can be used to create a Swing application similar to the above applet.
The JFrame class is a subclass of the AWT   Frame class and implements
the Accessible, WindowConstants,and RootPaneContainer interfaces. It uses
the BorderLayout manager for its content pane. The source code is presented below:
SwingApplication1.java

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;

public class SwingApplication1 extends JFrame


{
    public SwingApplication1()
     {
       super("Swing Application");
       Icon icon = new ImageIcon("1.124.gif", "1.124 GIF");
       JLabel label = new JLabel(" Test", icon, SwingConstants.CENTER);
       getContentPane().add(label);
     }
 
    public static void main(String args[])
     {
       final JFrame jfr = new SwingApplication1();
       jfr.setBounds(100,50,500,150);
       jfr.setVisible(true);
       jfr.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
       jfr.addWindowListener(new WindowAdapter()
                          & #160;        {
                          & #160;                 public void windowClosed(WindowEvent e)
                          & #160;                      {
                          & #160;                        ; System.exit(0);
                          & #160;                      }
                          & #160;              }               );
     }
}
As any Java® application, a main() method must be provided. In main() a JFrame is instantiated,
sized, and made visible. The default close operation is set to DISPOSE_ON_CLOSE (to dispose
any native resources when the window is closed), and a window listener is added in order to exit
the application as soon as the frame is closed.
Running the Java® interpreter (using java SwingApplication1) will execute the program and give
the following window:

Often, the source code file can be written in a way that it can be used both as an applet and an
application. The above example is rewritten in way to facilitate such a use. The source code is
provided below:

AppletApplication1.java

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class AppletApplication1 extends JApplet
{
    public void init()
     {
       Icon icon = new ImageIcon("1.124.gif", "1.124 GIF");
       JLabel label = new JLabel(" Test", icon, SwingConstants.CENTER);
       getContentPane().add(label);
     }
 
    public static void main(String args[])
     {
       final JFrame myFrame = new SwingApplication1();
       JApplet myApplet = new AppletApplication1();
       myApplet.init();
       myFrame.setContentPane(myApplet.getContentPane());
       myFrame.setBounds(100,50,500,150);
       myFrame.setVisible(true);
       myFrame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
       myFrame.addWindowListener(new WindowAdapter()
                          & #160;            {
                          & #160;               public void windowClosed(WindowEvent e)
                          & #160;                     {
                          & #160;                        ; System.exit(0);
                          & #160;                     }
                          & #160;            }                      );
     }
}

Mixing AWT and Swing


Attention should be given when mixing AWT and Swing components, since when lightweight
components overlap with heavyweight components, the heavyweight components are always
painted on top. In general, it should mixing AWT and Swing components should not be mixed
whenever possible. The "depth" at which components are displayed in a container is represented
by the Z-order. The latter is determined by the order with which each component is added to the
container, i.e. the first component to be added to a container has the highest Z-order which
means that it is displayed in front of all other components added to that container. When
lightweight and heavyweight components are mixed, the lightweight components, which need to
reside in a heavyweight container, have the same Z-order of their container, and within it the
order of each of the lightweight components is determined by the order in which they are added
to the container. Note, that whenever a container is extended and the paint() method is
overridden, the superclass paint() method should be explicitly invoked using super.paint() to
force drawing of the lightweight components.
When Swing popup menus, which are lightweight, are mixed with a heavyweight component, the
latter overlaps the former. This can be avoided by forcing the popup menus to be heavyweight
using the method setLightWeightPopupEnabled() of the JPopupMenu class. A similar problem
happens with a JScrollPane instance when any heavyweight components to it. Because there is
no option of setting the JScrollPane as a heavyweight, the AWT ScrollPane can be used instead,
which works fine with both lightweight and heavyweight components. Finally, heavyweight
components should be avoided (i.e. not be added) to Swing internal frames, i.e. to
a JInternalFrame instance.

Layout Managers
Layout managers (such as
the BorderLayout,   BoxLayout, GridLayout,  FlowLayout,   CardLayout,  GridBagLayout) are
used to determine the size and position of the components that are contained in a container. Each
container is provided a default layout manager, which, however, can be changed using
the setLayout() method and as an argument a newly created instance of the preferred layout
manager. The minimum, preferred, and maximum sizes of a component can also be specified to
the layout manager, as well as the preferred alignment.

Painting System
The AWT painting system controls the painting of a Swing GUI, starting with the highest
component that needs to be repainted and going down the hierarchy of containers, using the
event-dispatching thread. The painting of swing components occurs whenever necessary. Swing
uses double buffering to improve the efficiency and quality of the provided GUI. During
painting, each component paints itself before any of the components that it contains. A Swing
top-level container is painted on-screen using one of the methods: setVisible(true), show(),
orpack(). Painting starts with the highest component that needs to be repainted and moving down
the containment hierarchy, i.e. each component paints itself before any of the components it
contains.

Swing performs, by default, double-buffered to ensure smoothness and avoid flushing. Double
buffering is essentially implemented by an offscreen buffer in which all painting takes place, and
then it is flushed to the screen.

Event Handling
The event handling features of the Swing (and AWT) components allow to a Java® program to
respond to external events, such as the interaction of the user with the GUI. Events are handled
by event handlers that are registered with event sources. Any Swing component can be notified
for the occurrence of any event simply by implement the corresponding interface and registering
it as an event listener on a relevant event source. The latter is usually component, e.g. a button.
for each event there is a corresponding object which provides all the relevant information. An
event handler is implemented by:
 either
o implementing the corresponding listener interface, or
o extending a class that implements that interface
 implement the method in the listener (interface or subclass)
 adding that kind of listener to a component

Sometimes, anonymous inner classes are used to keep the code clearer and the event handler
closer to the point where it is registered. The event-handling code executes in the event-
dispatching thread (single thread) ensuring that each event handler will execute in sequence
allowing a previous event handler finish executing before the next one starts execution.
Therefore, the code in the event handlers should be either very short and quick to execute, or
another thread should be initiated to execute the code, in order to not harm the performance of
the program. Otherwise, painting, which also executes on the event-dispatching thread, will not
occur while an event is being handled.

Threads
Swing, in general, is not thread safe, because a Swing component can be accessed by only one
thread at a time, which, in general, is the event-dispatching thread. Because Swing is not thread
safe, Swing components, in general, should be accessed only from the event-dispatching thread.
The event-dispatching thread is the thread that invokes callback methods
(e.g. paint() and update() functions) and event handler methods. Swing has been designed to be
not thread safe in order to avoid the overhead of multithreading (e.g. obtaining and releasing
locks) and to simplify the subclassing of its component. It is not allowed to access Swing
components from any thread other than the event-dispatching thread.
However, some Swing components support multithread access. Actually after the realization of
a Swing component code that might affect or depend on the state of that component should be
executed in the event-dispatching thread. Realization of a component means after the component
is available for painting on screen, after it is painted or become ready to be painted using one of
the methods: setVisible(true), show(), orpack().
The access only through the event-dispatching thread is not required in the following cases:
 when dealing with thread safe methods (as specified in the Swing API documentation) to
construct and show a GUI in the main thread of  an application, as long as  no components
have been realized in the current runtime environment
 constructing and manipulating the GUI in an applet's init() method, as long as the
components have not been made visible, i.e. the method show() or setVisible(true) has never
been called on the actual applet object
 methods repaint() and revalidate() are safe to call from any thread
In general, Swing it is not safe to access Swing components from any thread other than the
event-dispatching thread. However, there are times that it is preferable to update Swing
components from another thread, or perform time-consuming operations on a separate thread,
and not use the event-dispatching thread. In those cases, Swing provides the
methods invokeLater() and invokeAndWait() in the SwingUtilities class, which can be used to
queue a runnable object on the event dispatch thread. They essentially allow a block of code
from another thread to be invoked and executed by the event-dispatching thread. Both methods
can be used to access Swing components from a thread other than the event-dispatching thread.
Method invokeLater() queues the runnable object and returns immediately, while invok
eAndWait() waits until the runnable object's run()method has started before returning. Only
the invokeLater() (and not invokeAndWait()) method can be called from the event dispatching
thread.
File I/O

Topics
1. Introduction
2. Text Input
3. Text Output
4. Binary Input and Output
1. Introduction

Java® uses a stream-based approach to input and output. A stream in this context is a flow of
data, which could either be read in from a data source (e.g. file, keyboard or socket) or written to
a data sink (e.g file, screen, or socket). Java® currently supports two types of streams:

 8-bit streams. These are intended for binary data i.e. data that will be manipulated at the
byte level. The abstract base classes for 8-bit streams are InputStream and OutputStream.
 16-bit streams. These are intended for character data. 16-bits streams are required
becuase Java®'s internal representation for characters is the 16-bit Unicode format rather
than the 8-bit ASCII format. The abstract base classes for 16-bit streams are Reader and
Writer.

It is possible to create a 16-bit Reader from an 8-bit InputStream using the InputStreamReader
class e.g.

Reader r = new InputStreamReader(System.in);       // System.in is an example of an


InputStream.

Likewise, it is possible to create a 16-bit Writer from an 8-bit OutputStream using the
OutputStreamWriter class e.g.

Writer w = new OutputStreamWriter(System.out);     // System.out is an example of an


OutputStream.
2. Text Input
The FileReader class is used to read characters from a file. This class can only read one 16-bit
Unicode character at a time (characters that are stored in 8-bit ASCII will be automatically
promoted to Unicode.) In order to read a full line of text at once, we must layer
a BufferedReader on top of the FileReader. Next, the individual words in the line of text can be
extracted using a StringTokenizer. If the text contains numbers, we must also
perform String toNumber conversion operations,
like Integer.parseInt() and Double.parseDouble().
import java.io.*;
import java.util.*;
  public class Main {
     public static void main(String[] args) {
          try {
                readText(args[0]);
          }
          catch (IOException e) {
                e.printStackTrace();
          }
     }
     // This function will read data from an ASCII text file.
     public static void readText(String fileName) throws IOException {
          // First create a FileReader.  A Reader is a 16-bit input stream,
          // which is intended for all forms of character (text) input.
          Reader reader = new FileReader(fileName);
          // Now create a BufferedReader from the Reader.  This allows us to
          // read in an entire line at a time.
          BufferedReader bufferedReader = new BufferedReader(reader);
          String nextLine;
          while ((nextLine = bufferedReader.readLine()) != null) {
                // Next, we create a StringTokenizer from the line we have just
                // read in.  This permits the extraction of nonspace characters.
                StringTokenizer tokenizer = new StringTokenizer(nextLine);
                // We can now extract various data types as follows.
                String companyName = tokenizer.nextToken();
                int numberShares = Integer.parseInt(tokenizer.nextToken());
                double sharePrice = Double.parseDouble(tokenizer.nextToken());
                // Print the data out on the screen.
                System.out.print(companyName + " has " + numberShares);
                System.out.println(" million shares valued at $" + sharePrice);
                // Close the file.
                bufferedReader.close();
          }
     }
}

This program can be easily converted to read in data from the keyboard. Simply replace

     Reader reader = new FileReader(fileName);

with

    Reader = new InputStreamReader(System.in);


3. Text Output
The FileWriter class is used to write text to a file. This class is only capable of writing out
individual characters and strings. We can layer a PrintWriter on top of the FileWriter, so that we
can write out numbers as well.
import java.io.*;
import java.util.*;
import java.text.*;
  public class Main {
     public static void main(String[] args) {
          try {
                writeText(args[0]);
          }
          catch (IOException e) {
                e.printStackTrace();
          }
     }
     // This function will write data to an ASCII text file.
     public static void writeText(String fileName) throws IOException {
          // First create a FileWriter.  A Writer is a 16-bit output stream,
          // which is intended for all forms of character (text) output.
          Writer writer = new FileWriter(fileName);
          // Next create a PrintWriter from the Writer.  This allows us to
          // print out other data types besides characters and Strings.
          PrintWriter printWriter = new PrintWriter(writer);
          // Now print out various data types.
          boolean b = true;
          int i = 20;
          double d = 1.124;
          String str = "This is some text.";
          printWriter.print(b);
          printWriter.print(i);
          printWriter.print(d);
          printWriter.println("\n" + str);
          // This is an example of formatted output.  In the format string,
          // 0 and # represent digits.  # means that the digit should not
          // be displayed if it is 0.
          DecimalFormat df = new DecimalFormat("#.000");
          printWriter.println(df.format(200.0));  // 200.000
          printWriter.println(df.format(0.123));  // .123
          // This will flush the PrintWriter's internal buffer, causing the
          // data to be actually written to file.
          printWriter.flush();
          // Finally, close the file.
          printWriter.close();
     }
}
4. Binary Input and Output
Binary input and output is done using the 8-bit streams. To read binary data from a file, we
create a  FileInputStreamand then layer a DataInputStream on top of it. To write binary data to
a file, we create a FileOutputStream and then layer a DataOutputStream on top of it. The
following example illustrates this.
import java.io.*;
  public class Main {
     public static void main(String[] args) {
          try {
                writeBinary(args[0]);
                readBinary(args[0]);
          }
          catch (IOException e) {
                e.printStackTrace();
          }
     }
     // This function will write binary data to a file.
     public static void writeBinary(String fileName) throws IOException {
          // First create a FileOutputStream.
          OutputStream outputStream = new FileOutputStream(fileName);
          // Now layer a DataOutputStream on top of it.
          DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
          // Now write out some data in binary format.  Strings are written out
          // in UTF format, which is a bridge between ASCII and Unicode.
          int i = 5;
          double d = 1.124;
          char c = 'z';
          String str = "Some text";
          dataOutputStream.writeInt(i);            // Increases file size by 4 bytes.
          dataOutputStream.writeDouble(d);   // Increases file size by 8 bytes.
          dataOutputStream.writeChar(c);       // Increases file size by 2 bytes.
          dataOutputStream.writeUTF(str);     // Increases file size by 2+9 bytes.
          // Close the file.
          dataOutputStream.close();
     }
     // This function will read binary data from a file.
     public static void readBinary(String fileName) throws IOException {
          // First create a FileInputStream.
          InputStream inputStream = new FileInputStream(fileName);
          // Now layer a DataInputStream on top of it.
          DataInputStream dataInputStream = new DataInputStream(inputStream);
          // Now read in data from the binary file.
          int i;
          double d;
          char c;
          String str;
          i = dataInputStream.readInt();
          d = dataInputStream.readDouble();
          c = dataInputStream.readChar();
          str = dataInputStream.readUTF();
          System.out.print("integer " + i + " double " + d);
          System.out.println(" char " + c + " String " + str);
          // Close the file.
          dataInputStream.close();
     }
}

Multithreading

Topics
1. Threads, Processes and Multitasking
2. How to Create Threads
3. The LifeCycle of a Thread
4. Animations
1. Threads, Processes and Multitasking
Multitasking is the ability of a computer's operating system to run several programs
(or processes) concurrently on a single CPU. This is done by switching from one program to
another fast enough to create the appearance that all programs are executing
simultaneously. There are two types of multitasking:
Preemptive multitasking. In preemptive multitasking, the operating system decides how to
allocate CPU time slices to each program. At the end of a time slice, the currently active program
is forced to yield control to the operating system, whether it wants to or not. Examples of
operating systems that support premptive multitasking are Unix®, Windows® 95/98, Windows®
NT and the planned release of Mac® OS X.
Cooperative multitasking. In cooperative multitasking, each program controls how much CPU
time it needs. This means that a program must cooperate in yielding control to other programs, or
else it will hog the CPU. Examples of operating systems that support cooperative multitasking
are Windows® 3.1 and Mac® OS 8.5.
Multithreading extends the concept of multitasking by allowing individual programs to perform
several tasks concurrently. Each task is referred to as a thread and it represents a separate flow of
control. Multithreading can be very useful in practical applications. For example, if a web page is
taking too long to load in a web browser, the user should be able interrupt the loading of the page
by clicking on the stop button. The user interface can be kept responsive to the user by using a
separate thread for the network activity needed to load the page.
What then is the difference then between a process and a thread? The answer is that each process
has its own set of variables, whereas threads share the same data and system resources. A
multithreaded program must therefore be very careful about the way that threads access and
modify data, or else unpredictable behavior may occur.
2. How to Create Threads
(Ref.  Java® Tutorial)
We can create a new thread using the Thread class provided in the java.lang package. There are
two ways to use the Thread class.
 By creating a subclass of Thread.
 By writing a class that implements the Runnable interface.
Subclassing the Thread class
In this approach, we create a subclass of the Thread class. The Thread class has a method
named run(), which we can override in our subclass. Our implementation of the run() method
must contain all code that is to be executed within the thread.
class MyClass extends Thread {
     // ...
     public void run() {
          // All code to be executed within the thread goes here.
     }
}
 
We can create a new thread by instantiating our class, and we run it by calling the start() method
that we inherited from class Thread.
MyClass a = new MyClass();
a.start();
This approach for creating a thread works fine from a technical standpoint. Conceptually,
however, it does not make that much sense to say that MyClass "is a" Thread. All that we are
really interested in doing is to provide arun() method that the Thread class can execute. The next
approach is geared to do exactly this.
Implementing the Runnable Interface
In this approach, we write a class that implements the Runnable interface.
The Runnable interface requires us to implement a single method named run(), within which we
place all code that is to be executed within the thread.
class MyClass implements Runnable {
     // ...
     public void run() {
          // All code to be executed within the thread goes here.
     }
}
 
We can create a new thread by creating a Thread object from an object of type MyClass. We run
the thread by calling the Thread object's start() method.
MyClass a = new MyClass;
Thread t = new Thread(a);
t.start();
3. The LifeCycle of a Thread
(Ref.  Java® Tutorial)

A thread can be in one of four states during its lifetime:

 new - A new thread is one that has been created (using the new operator), but has not yet
been started.
 runnable - A thread becomes runnable once its start() method has been invoked. This
means that the code in the run() method can execute whenever the thread receives CPU time
from the operating system.
 blocked - A thread can become blocked if one of the following events occurs:
 The thread's sleep() method is invoked. In this case, the thread remains blocked until the
specified number of milliseconds elapses.
 The thread calls the wait() method of an object. In this case, the thread remains blocked
until either the object's notify() method or its notifyAll() method is called from another
thread. The calls to wait(), notify() andnotifyAll() are typically found
within synchronized methods of the object.
 The thread has blocked on an input/output operation. In this case, the thread remains
blocked until the i/o operation has completed.

 dead - A thread typically dies when the run() method has finished executing.

Note: The following methods in the java.lang.Thread class should no longer be used, since they
can lead to unpredicable behavior: stop(), suspend() and resume().
The following example illustrates various thread states. The main thread in our program creates a
new thread,Thread-0. It then starts Thread-0, thereby making Thread-0 runnable so that it prints
out an integer every 500 milliseconds. We call the sleep() method to enforce the 500 millisecond
delay between printing two consecutive integers. In the meantime, the main thread proceeds to
print out an integer every second only. The output from the program shows that the two threads
are running in parallel. When the main thread finishes its for loop, it stops Thread-0.
We maintain a variable, myThread, which initially references Thread-0. This variable is polled
by the run() method to make sure that it is still referencing Thread-0. All we have to do to stop
the thread is to set myThread to null. This will cause the run() method to terminate normally.
class MyClass implements Runnable {
     int i;
     Thread myThread;
     public MyClass() {
          i = 0;
     }
     // This will terminate the run() method.
     public void stop() {
          myThread = null;
     }
     // The run() method simply prints out a sequence of integers, one every half second.
     public void run() {
          // Get a handle on the thread that we are running in.
          myThread = Thread.currentThread();
          // Keep going as long as myThread is the same as the current thread.
          while (Thread.currentThread() == myThread) {
                System.out.println(Thread.currentThread().getName() + ": " + i);
                i++;
                try {
                     Thread.sleep(500); // Tell the thread to sleep for half a second.
                }
                catch (InterruptedException e) {}
          }
     }
}
 
class Threadtest {
     public static void main(String[] args) {
          MyClass a = new MyClass();
          Thread t = new Thread(a);
          // Start another thread.  This thread will run in parallel to the main thread.
          System.out.println(Thread.currentThread().getName() + ": Starting a separate thread");
          t.start();
          // The main thread proceeds to print out a sequence of integers of its own, one every
second.
          for (int i = 0; i < 6; i++) {
                System.out.println(Thread.currentThread().getName() + ": " + i);
                // Tell the main thread to sleep for a second.
                try {
                     Thread.sleep(1000);
                }
                catch (InterruptedException e) {}
          }
          // Stop the parallel thread.  We do this by setting myThread to null in our runnable object.
          System.out.println(Thread.currentThread().getName() + ": Stopping the thread");
          a.stop();
     }
}
4. Animations

Here is an example of a simple animation. We have used a separate thread to control the motion
of a ball on the screen.

anim.html
<HTML>
<BODY>
<APPLET CODE="Animation.class" WIDTH=300 HEIGHT=400>
</APPLET>
</BODY>
 
Animation.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
  public class Animation extends JApplet implements Runnable, ActionListener {
     int miFrameNumber = -1;
     int miTimeStep;
     Thread mAnimationThread;
     boolean mbIsPaused = false;
     Button mButton;
     Point mCenter;
     int miRadius;
     int miDX, miDY;
     public void init() {
          // Make the animation run at 20 frames per second.  We do this by
          // setting the timestep to 50ms.
          miTimeStep = 50;
          // Initialize the parameters of the circle.
          mCenter = new Point(getSize().width/2, getSize().height/2);
          miRadius = 15;
          miDX = 4;  // X offset per timestep.
          miDY = 3;  // Y offset per timestep.
          // Create a button to start and stop the animation.
          mButton = new Button("Stop");
          getContentPane().add(mButton, "North");
          mButton.addActionListener(this);
          // Create a JPanel subclass and add it to the JApplet.  All drawing
          // will be done here, do we must write the paintComponent() method.
          // Note that the anonymous class has access to the private data of
          // class Animation, because it is defined locally.
          getContentPane().add(new JPanel() {
                public void paintComponent(Graphics g) {
                     // Paint the background.
                     super.paintComponent(g);
                     // Display the frame number.
                     g.drawString("Frame " + miFrameNumber, getSize().width/2 - 40,
                                             getSize().height - 15);
                     // Draw the circle.
                     g.setColor(Color.red);
                     g.fillOval(mCenter.x-miRadius, mCenter.y-miRadius, 2*miRadius,
                                      2*miRadius);
                }
          }, "Center");
     }
     public void start() {
          if (mbIsPaused) {
                // Don't do anything.  The animation has been paused.
          } else {
                // Start animating.
                if (mAnimationThread == null) {
                     mAnimationThread = new Thread(this);
                }
                mAnimationThread.start();
          }
     }
     public void stop() {
          // Stop the animating thread by setting the mAnimationThread variable
          // to null.  This will cause the thread to break out of the while loop,
          // so that the run() method terminates naturally.
          mAnimationThread = null;
     }
     public void actionPerformed(ActionEvent e) {
          if (mbIsPaused) {
                mbIsPaused = false;
                mButton.setLabel("Stop");
                start();
          } else {
                mbIsPaused = true;
                mButton.setLabel("Start");
                stop();
          }
     }
     public void run() {
          // Just to be nice, lower this thread's priority so it can't
          // interfere with other processing going on.
          Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
          // Remember the starting time.
          long startTime = System.currentTimeMillis();
          // Remember which thread we are.
          Thread currentThread = Thread.currentThread();
          // This is the animation loop.
          while (currentThread == mAnimationThread) {
                // Advance the animation frame.
                miFrameNumber++;
                // Update the position of the circle.
                move();
                // Draw the next frame.
                repaint();
                // Delay depending on how far we are behind.
                try {
                     startTime += miTimeStep;
                     Thread.sleep(Math.max(0,
                                     startTime-System.currentTimeMillis()));
                }
                catch (InterruptedException e) {
                     break;
                }
          }
     }
     // Update the position of the circle.
     void move() {
          mCenter.x += miDX;
          if (mCenter.x - miRadius < 0 ||
                mCenter.x + miRadius > getSize().width) {
                miDX = -miDX;
                mCenter.x += 2*miDX;
          }
          mCenter.y += miDY;
          if (mCenter.y - miRadius < 0 ||
                mCenter.y + miRadius > getSize().height) {
                miDY = -miDY;
                mCenter.y += 2*miDY;
          }
     }
}

You might also like