Interface Package-1

Download as pdf or txt
Download as pdf or txt
You are on page 1of 24

Module III

Interfaces
Introduction
Using the keyword interface, you can fully abstract a class’ interface from its
implementation.That is, using interface, you can specify what a class must do, but not how it
does it.
Once it is defined, any number of classes can implement an interface. Also, one class can
implement any number of interfaces.To implement an interface, a class must create the
complete set of methods defined bythe interface. However, each class is free to determine the
details of its own implementation.
By providing the interface keyword, Java allows you to fully utilize the “one interface,
multiple methods” aspect of polymorphism.

Defining an Interface
An interface is defined much like a class. This is the general form of an interface:
access interface name {
return-type method-name1(parameter-list);
return-type method-name2(parameter-list);
type final-varname1 = value;
type final-varname2 = value;
// ...
return-type method-nameN(parameter-list);
type final-varnameN = value;
}

When no access specifier is included, then default access results, and the interface is only
available to other members of the package in which it is declared. When it is declared as
public, the interface can be used by any other code. In this case, the interface must be the
only public interface declared in the file, and the file must have the same name as the interface.
name is the name of the interface, and can be any valid identifier. Notice that the methods that
are declared have no bodies. They end with a semicolon after the parameter list. They are,
essentially, abstract methods; there can be no default implementation of any method specified
within an interface. Each class that includes an interface must implement all of the methods.
Variables can be declared inside of interface declarations. They are implicitly final and
static, meaning they cannot be changed by the implementing class. They must also be
initialized. All methods and variables are implicitly public.

Here is an example of an interface definition. It declares a simple interface that contains


one method called callback( ) that takes a single integer parameter.
interface Callback {

void callback(int param);


}

Implementing Interfaces
Once an interface has been defined, one or more classes can implement that interface. To
implement an interface, include the implements clause in a class definition, and then create
the methods defined by the interface. The general form of a class that includes the
implementsclause looks like this:

class classname [extends superclass] [implements interface [,interface...]] {


// class-body
}
If a class implements more than one interface, the interfaces are separated with a comma.

Example:
interface Callback {

void callback(int param);


}

class Client implements Callback {


// Implement Callback's interface
public void callback(int p) {

System.out.println("callback called with " + p);

}
}
class TestIface {
public static void main(String args[]) {
Callback c = new Client();
c.callback(42);
}
}
The output of this program is shown here:
callback called with 42
example 2
the below program explain about dynamic polymorphism.

interface Callback {

void callback(int param);


}

class Client implements Callback {


// Implement Callback's interface
public void callback(int p) {

System.out.println("callback called with " + p);

}
}
// Another implementation of Callback.
class AnotherClient implements Callback {
// Implement Callback's interface
public void callback(int p) {
System.out.println("Another version of callback");
System.out.println("p squared is " + (p*p));
}
}
class TestIface2 {
public static void main(String args[]) {
Callback c = new Client();
AnotherClient ob = new AnotherClient();
c.callback(42);
c = ob; // c now refers to AnotherClient object
c.callback(42);
}
}

The output from this program is shown here:


callback called with 42
Another version of callback
p squared is 1764
Example of Multiple Inheritance using interface

interface X
{
public void myMethod();
}
interface Y
{
public void myMethod1();
}
class vj3 implements X, Y
{
public void myMethod()
{
System.out.println("Implementing more than one interfaces one is
x");
}
public void myMethod1()
{
System.out.println("Implementing more than one interfaces ther is
y");
}

public static void main(String args[]){


vj3 obj = new vj3();
obj.myMethod();
obj.myMethod1();
}
}

Example4:stack operations

// Define an integer stack interface.

interface IntStack {
void push(int item); // store an item
int pop(); // retrieve an item
}
// An implementation of IntStack that uses fixed storage.
class FixedStack implements IntStack {
private int stck[];
private int tos;
// allocate and initialize stack
FixedStack(int size) {
stck = new int[size];
tos = -1;
}

// Push an item onto the stack


public void push(int item) {
if(tos==stck.length-1) // use length member
System.out.println("Stack is full.");
else
stck[++tos] = item;
}
// Pop an item from the stack
public int pop() {
if(tos < 0) {
System.out.println("Stack underflow.");
return 0;
}
else
return stck[tos--];
}
}

class IFTest {

public static void main(String args[]) {


FixedStack mystack1 = new FixedStack(5);
FixedStack mystack2 = new FixedStack(8);
// push some numbers onto the stack
for(int i=0; i<5; i++)
mystack1.push(i);
for(int i=0; i<8; i++)
mystack2.push(i);
// pop those numbers off the stack
System.out.println("Stack in mystack1:");
for(int i=0; i<5; i++)
System.out.println(mystack1.pop());
System.out.println("Stack in mystack2:");
for(int i=0; i<8; i++)
System.out.println(mystack2.pop());
}
}

extending interfaces

// One interface can extend another.


interface A
{
void meth1();
void meth2();
}
// B now includes meth1() and meth2() -- it adds meth3().
interface B extends A
{
void meth3();
}
// This class must implement all of A and B
class MyClass implements B
{
public void meth1() {
System.out.println("Implement meth1().");
}
public void meth2()
{
System.out.println("Implement meth2().");
}
public void meth3()
{
System.out.println("Implement meth3().");
}
}

class IFExtend
{
public static void main(String arg[])
{
MyClass ob = new MyClass();
ob.meth1();
ob.meth2();
ob.meth3();
}
}
Package:
A package is a namespace that organizes a set of related classes and interfaces. Conceptually
you can think of packages as being similar to different folders on your computer.
Defining a Package
To create a package is quite easy: simply include a package command as the first statement
in a Java source file. Any classes declared within that file will belong to the specified
package.
The package statement defines a name space in which classes are stored.

This is the general form of the package statement:


package pkg;

A Short Package Example


Keeping the preceding discussion in mind, you can try this simple package:
// A simple package
package MyPack;

class Balance {
String name;
double bal;
Balance(String n, double b) {
name = n;
bal = b;
}
void show() {
if(bal<0)
System.out.print("--> ");
System.out.println(name + ": $" + bal);
}
}

class AccountBalance
{
public static void main(String args[])
{
Balance current[] = new Balance[3];
current[0] = new Balance("K. J. Fielding", 123.23);
current[1] = new Balance("Will Tell", 157.02);
current[2] = new Balance("Tom Jackson", -12.33);
for(int i=0; i<3; i++) current[i].show();
}
}
ACCESS PROTECTION
Classes and packages are both means of encapsulating and containing the name space
and scope of variables and methods. Packages act as containers for classes and other
subordinate packages. Classes act as containers for data and code. The class is Java’s
smallest unit of abstraction. Because of the interplay between classes and packages, Java
addresses four categories of visibility for class members:
• Subclasses in the same package
• Non-subclasses in the same package
• Subclasses in different packages
• Classes that are neither in the same package nor subclasses

TABLE:-Class Member Access

The following example shows all combinations of the access control modifiers. This example
has two packages and five classes. Remember that the classes for the two different
packages need to be stored in directories named after their respective packages—in this
case, p1 and p2.
The source for the first package defines three classes: Protection, Derived, and SamePackage.
The first class defines four int variables in each of the legal protection modes. The variable n
is declared with the default protection, n_pri is private, n_pro is protected, and n_pub is
public.

EXAMPLE:~
This is file Protection.java:
package p1;
public class Protection {
int n = 1;
private int n_pri = 2;
protected int n_pro = 3;
public int n_pub = 4;
public Protection() {
System.out.println("base constructor");
System.out.println("n = " + n);
System.out.println("n_pri = " + n_pri);
System.out.println("n_pro = " + n_pro);
System.out.println("n_pub = " + n_pub);
}
}

Same package subclass

This is file Derived.java:


package p1;
class Derived extends Protection {
Derived() {
System.out.println("derived constructor");
System.out.println("n = " + n);
// class only
// System.out.println("n_pri = "4 + n_pri);
System.out.println("n_pro = " + n_pro);
System.out.println("n_pub = " + n_pub);
}
}

Same package nonsubclass

This is file SamePackage.java:


package p1;
class SamePackage {
SamePackage() {
Protection p = new Protection();
System.out.println("same package constructor");
System.out.println("n = " + p.n);
// class only
// System.out.println("n_pri = " + p.n_pri);
System.out.println("n_pro = " + p.n_pro);
System.out.println("n_pub = " + p.n_pub);
}

The demo class which consist of main method()


package p1;
// Instantiate the various classes in p1.
public class Demo {
public static void main(String args[]) {
Protection ob1 = new Protection();
Derived ob2 = new Derived();
SamePackage ob3 = new SamePackage();
}
}
Following is the source code for the other package, p2. The two classes defined in p2
cover the other two conditions that are affected by access control. The first class, Protection2,
isa subclass of p1.Protection. This grants access to all of p1.Protection’s variables except
forn_pri (because it is private) and n, the variable declared with the default protection.
Remember,the default only allows access from within the class or the package, not extra-
packagesubclasses. Finally, the class OtherPackage has access to only one variable, n_pub,
whichwas declared public.

Different package subclasses


This is file Protection2.java:
package p2;
class Protection2 extends p1.Protection {
Protection2() {
System.out.println("derived other package constructor");
// class or package only
// System.out.println("n = " + n);
// class only
// System.out.println("n_pri = " + n_pri);
System.out.println("n_pro = " + n_pro);
System.out.println("n_pub = " + n_pub);
}
}

Different package nonsubclasses

This is file OtherPackage.java:


package p2;
class OtherPackage {
OtherPackage() {
p1.Protection p = new p1.Protection();
System.out.println("other package constructor");
// class or package only
// System.out.println("n = " + p.n);
// class only
// System.out.println("n_pri = " + p.n_pri);
// class, subclass or package only
// System.out.println("n_pro = " + p.n_pro);
System.out.println("n_pub = " + p.n_pub);
}
}

The class which consists of main method


package p2;
// Instantiate the various classes in p2.
public class Demo {
public static void main(String args[]) {
Protection2 ob1 = new Protection2();
OtherPackage ob2 = new OtherPackage();
}
}
importing a package and user defined package

Given that packages exist and are a good mechanism for compartmentalizing diverse classes
from each other, it is easy to see why all of the built-in Java classes are stored in packages.
There are no core Java classes in the unnamed default package; all of the standard classes
are stored in some named package. Since classes within packages must be fully qualified
with their package name or names, it could become tedious to type in the long dot-separated
package path name for every class you want to use. For this reason, Java includes the import
statement to bring certain classes, or entire packages, into visibility. Once imported, a class
can be referred to directly, using only its name. The import statement is a convenience to
the programmer and is not technically needed to write a complete Java program. If you are
going to refer to a few dozen classes in your application, however, the import statement will
save a lot of typing.

In a Java source file, import statements occur immediately following the package statement
(if it exists) and before any class definitions. This is the general form of the import statement:

import pkg1[.pkg2].(classname|*);

Here, pkg1 is the name of a top-level package, and pkg2 is the name of a subordinate
package inside the outer package separated by a dot (.). There is no practical limit on the
depth of a package hierarchy, except that imposed by the file system.

package MyPack;
public class Balance {
String name;
double bal;
public Balance(String n, double b) {
name = n;
bal = b;
}
public void show() {
if(bal<0)
System.out.print("--> ");
System.out.println(name + ": $" + bal);
}
}
As you can see, the Balance class is now public. Also, its constructor and its show( )
method are public, too. This means that they can be accessed by any type of code outside
the MyPack package. For example, here TestBalance imports MyPack and is then able to
make use of the Balance class:
import MyPack.*;
class TestBalance {
public static void main(String args[]) {
/* Because Balance is public, you may use Balance
class and call its constructor. */
Balance test = new Balance("J. J. Jaspers", 99.88);
test.show(); // you may also call show()
}
}

Threads:

Introduction to threads

A multithreaded program contains two or more parts that can run


concurrently. Each part of such a program is called a thread, and each thread defines
a separate path of execution. Thus, multithreading is a specialized form of multitasking.
You are almost certainly acquainted with multitasking, because it is supported by virtually
all modern operating systems. However, there are two distinct types of multitasking:
processbased
and thread-based. It is important to understand the difference between the two. For
most readers, process-based multitasking is the more familiar form. A process is, in essence,
a program that is executing.

In a thread-based multitasking environment, the thread is the smallest unit of dispatchable


code. This means that a single program can perform two or more tasks simultaneously. For
instance, a text editor can format text at the same time that it is printing, as long as these
two actions are being performed by two separate threads.

Multitasking threads require less overhead than multitasking processes. Processes are
heavyweight tasks that require their own separate address spaces. Interprocess
communication
is expensive and limited. Context switching from one process to another is also costly.
Threads,
on the other hand, are lightweight. They share the same address space and cooperatively
share the same heavyweight process.
Multithreading enables you to write very efficient programs that make maximum use of
the CPU, because idle time can be kept to a minimum. This is especially important for the
interactive, networked environment in which Java operates, because idle time is common.

creating threads

The Java run-time system depends on threads for many things, and all the class libraries
are designed with multithreading in mind. In fact, Java uses threads to enable the entire
environment to be asynchronous. This helps reduce inefficiency by preventing the waste
of CPU cycles.
The value of a multithreaded environment is best understood in contrast to its counterpart.
Single-threaded systems use an approach called an event loop with polling. In this model, a
single thread of control runs in an infinite loop, polling a single event queue to decide what
to do next. Once this polling mechanism returns with, say, a signal that a network file is
ready to be read, then the event loop dispatches control to the appropriate event handler.
Until this event handler returns, nothing else can happen in the system. This wastes CPU
time. It can also result in one part of a program dominating the system and preventing any
other events from being processed. In general, in a singled-threaded environment, when a
thread blocks (that is, suspends execution) because it is waiting for some resource, the entire
program stops running.
The benefit of Java’s multithreading is that the main loop/polling mechanism is eliminated.
One thread can pause without stopping other parts of your program. For example, the idle
time created when a thread reads data from a network or waits for user input can be utilized
elsewhere. Multithreading allows animation loops to sleep for a second between each frame
without causing the whole system to pause. When a thread blocks in a Java program, only
the single thread that is blocked pauses. All other threads continue to run.
Threads exist in several states. A thread can be running. It can be ready to run as soon as
it gets CPU time. Arunning thread can be suspended, which temporarily suspends its activity.
Asuspended thread can then be resumed, allowing it to pick up where it left off. A thread
can be blocked when waiting for a resource. At any time, a thread can be terminated, which
halts its execution immediately.

Thread: Java provides built-in support for multi-threaded programming. A multi-threaded


program contains two or more parts that can run concurrently. Each part of that program is
called a thread and each thread defines a separate path of execution.
Threads are present in a package.
Creating a thread: A thread is created by instantiating an object of class Thread. Java
defines two ways through which this can be accomplished:
1) By implementing the Runnable interface
2) By extending the class Thread, itself
Implementing Runnable:
Class NewThread implements Runnable{
Thread t;
NewThread(){
t=new Thread(this,”Demo Thread”);
System.out.println(“child thread:”+t);
t.start();
}
Public void run(){
try{
for(i=5;i>0;i--)
{
System.out.println(“child Thread”+i);
Thread.sleep(500);
}
}catch(InterruptedException e){
System.out.println(“child thread interrupted”);
}
System.out.println(“Exiting child thread”);
}
}
Class threadDemo{
Public static void main(String args[])
{
New NewThread();
try{
for(i=5;i>0;i--)
{
System.out.println(“Main Thread:”+i);
Thread.sleep(1000);
}
}catch(InterruptedException e){
System.out.println(“Main thread interrupted”);
}
System.out.println(“Main thread exiting”);
}
OUTPUT:-
Child thread:Thread[Demo thread,5,main]
Main Thread:5
Child Thread:5
Child Thread:4
Main Thread:4
Child Thread:3
Child Thread:2
Main Thread:3
Child Thread:1
Exiting child thread
Main Thread:2
Main Thread:1
Main Thread exiting

Extending Thread:-
Class NewThread extends Thread{
Thread t;
NewThread(){
super(”Demo Thread”);
System.out.println(“child thread:”+this);
start();
}
Public void run(){
try{
for(i=5;i>0;i--)
{
System.out.println(“child Thread”+i);
Thread.sleep(500);
}
}catch(InterruptedException e){
System.out.println(“child thread interrupted”);
}
System.out.println(“Exiting child thread”);
}
}
Class threadDemo{
Public static void main(String args[])
{
New NewThread();
try{
for(i=5;i>0;i--)
{
System.out.println(“Main Thread:”+i);
Thread.sleep(1000);
}
}catch(InterruptedException e){
System.out.println(“Main thread interrupted”);
}
System.out.println(“Main thread exiting”);
}
OUTPUT
Child thread:Thread[Demo thread,5,main]
Main Thread:5
Child Thread:5
Child Thread:4
Main Thread:4
Child Thread:3
Child Thread:2
Main Thread:3
Child Thread:1
Exiting child thread
Main Thread:2
Main Thread:1
Main Thread exiting

Main thread: When a Java program starts up, one thread begins running immediately. This
is usuallycalled the main thread of your program, because it is the one that is executed when
your
program begins. The main thread is important for two reasons:
• It is the thread from which other “child” threads will be spawned.
• Often, it must be the last thread to finish execution because it performs various
shutdown actions.

Creating multiple threads:


classNewThread implements Runnable {
String name; // name of thread
Thread t;
NewThread(String threadname) {
name = threadname;
t = new Thread(this, name);
System.out.println("New thread: " + t);
t.start(); // Start the thread
}
public void run() {
try {
for(inti = 5; i> 0; i--) {
System.out.println(name + ": " + i);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
System.out.println(name + "Interrupted");
}
System.out.println(name + " exiting.");
}
}
classMultiThreadDemo {
public static void main(String args[]) {
newNewThread("One"); // start threads
newNewThread("Two");
newNewThread("Three");
try {
// wait for other threads to end
Thread.sleep(10000);
} catch (InterruptedException e) {
System.out.println("Main thread Interrupted");
}
System.out.println("Main thread exiting.");
}
}
The output from this program is shown here:
New thread: Thread[One,5,main]
New thread: Thread[Two,5,main]
New thread: Thread[Three,5,main]
One: 5
Two: 5
Three: 5
One: 4
Two: 4
Three: 4
One: 3
Three: 3
Two: 3
One: 2
Three: 2
Two: 2
One: 1
Three: 1
Two: 1
One exiting.
Two exiting.
Three exiting.
Main thread exiting.
Synchronization: When two or more threads need access to a shared resource, they need
some way to ensure that the resource will be used by only one thread at a time. The process
by which this is achieved is called synchronization.
Implementing synchronization:
SynchronizationiseasyinJava,becauseallobjectshavetheirownimplicitmonitorassociated with
them. To enter an object’s monitor, just call a method that has been modified with the
synchronized keyword. While a thread is inside a synchronized method, all other threads that
try to call it (or any other synchronized method) on the same instance have to wait. To exit
the monitor and relinquish control of the object to the next waiting thread, the owner of the
monitor simply returns from the synchronized method.
Example:
classCallme {
Synchronized void call(String msg) {
System.out.print("[" + msg);
try {
Thread.sleep(1000);
} catch(InterruptedException e) {
System.out.println("Interrupted");
}
System.out.println("]");
}
}
class Caller implements Runnable {
String msg;
Callme target;
Thread t;
public Caller(Callmetarg, String s) {
target = targ;
msg = s;
t = new Thread(this);
t.start();
}
public void run() {
target.call(msg);
}
}
class Synch {
public static void main(String args[]) {
Callme target = new Callme();
Caller ob1 = new Caller(target, "Hello");
Caller ob2 = new Caller(target, "Synchronized");
Caller ob3 = new Caller(target, "World");
// wait for threads to end
try {
ob1.t.join();
ob2.t.join();
ob3.t.join();
} catch(InterruptedException e) {
System.out.println("Interrupted");
}
}
}
Here is the output produced by this program
[Hello]
[Synchronized]
[World]

IsAlive() and join():Two ways exist to determine whether a thread has finished. First, you
can call isAlive( ) on the thread. This method is defined by Thread, and its general form is
shown here:
finalbooleanisAlive( )
TheisAlive()methodreturnstrueifthethreaduponwhichitiscalledisstillrunning.Itreturns
falseotherwise. While isAlive( ) is occasionally useful, the method that you will more
commonly use to wait for a thread to finish is called join( ), shown here:
final void join( ) throws InterruptedException
This method waits until the thread on which it is called terminates. Its name comes from the
concept of the calling thread waiting until the specified thread joins it. Additional forms of
join()allowyoutospecifyamaximumamountoftimethatyouwanttowaitforthespecified thread to
terminate. Hereisanexamplethatuses join()toensurethatthe
mainthreadisthelasttostop.Italsodemonstratesthe isAlive()method.
// Using join() to wait for threads to finish.
classNewThread implements Runnable {
String name; // name of thread
Thread t;
NewThread(String threadname) {
name = threadname;
t = new Thread(this, name);
System.out.println("New thread: " + t);
t.start(); // Start the thread
}
// This is the entry point for thread.
public void run() {
try {
for(inti = 5; i> 0; i--) {
System.out.println(name + ": " + i);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
System.out.println(name + " interrupted.");
}
System.out.println(name + " exiting.");
}
}
classDemoJoin {
public static void main(String args[]) {
NewThread ob1 = new NewThread("One");
NewThread ob2 = new NewThread("Two");
NewThread ob3 = new NewThread("Three");
System.out.println("Thread One is alive: " + ob1.t.isAlive());
System.out.println("Thread Two is alive: " + ob2.t.isAlive());
System.out.println("Thread Three is alive: " + ob3.t.isAlive());
// wait for threads to finish
try {
System.out.println("Waiting for threads to finish.");
ob1.t.join();
ob2.t.join();
ob3.t.join();
} catch (InterruptedException e) {
System.out.println("Main thread Interrupted");
}
System.out.println("Thread One is alive: " + ob1.t.isAlive());
System.out.println("Thread Two is alive: " + ob2.t.isAlive());
System.out.println("Thread Three is alive: " + ob3.t.isAlive());
System.out.println("Main thread exiting.");
}
}
Sample output from this program is shown here.
New thread: Thread[One,5,main]
New thread: Thread[Two,5,main]
New thread: Thread[Three,5,main]
Thread One is alive: true
Thread Two is alive: true
Thread Three is alive: true
Waiting for threads to finish.
One: 5
Two: 5
Three: 5
One: 4
Two: 4
Three: 4
One: 3
Two: 3
Three: 3
One: 2
Two: 2
Three: 2
One: 1
Two: 1
Three: 1
Two exiting.
Three exiting.
One exiting.
Thread One is alive: false
Thread Two is alive: false
Thread Three is alive: false
Main thread exiting.
As you can see, after the calls to join( ) return, the threads have stopped executing.

Thread Priorities
Thread priorities are used by the thread scheduler to decide when each thread should be
allowed to run. In theory, higher-priority threads get more CPU time than lower-priority
threads. In practice, the amount of CPU time that a thread gets often depends on several
factors besides its priority. (For example, how an operating system implements multitasking
can affect the relative availability of CPU time.) A higher-priority thread can also preempt a
lower-priority one. For instance, when a lower-priority thread is running and a higher-priority
thread resumes (from sleeping or waiting on I/O, for example), it will preempt the
lowerpriority
thread.

To set a thread’s priority, use the setPriority( ) method, which is a member of Thread.
This is its general form:
final void setPriority(int level)
Here, level specifies the new priority setting for the calling thread. The value of level must be
within the range MIN_PRIORITY and MAX_PRIORITY. Currently, these values are 1
and
10, respectively. To return a thread to default priority, specify NORM_PRIORITY, which is
currently 5. These priorities are defined as static final variables within Thread.
You can obtain the current priority setting by calling the getPriority( ) method of Thread,
shown here:
final int getPriority( )

// Demonstrate thread priorities.


class clicker implements Runnable {
long click = 0;
Thread t;
private volatile boolean running = true;
public clicker(int p) {
t = new Thread(this);
t.setPriority(p);
}
public void run() {
while (running) {
click++;
}
}
public void stop() {
running = false;
}
public void start() {
t.start();
}
}
class HiLoPri {
public static void main(String args[]) {
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
clicker hi = new clicker(Thread.NORM_PRIORITY + 2);
clicker lo = new clicker(Thread.NORM_PRIORITY - 2);
lo.start();
hi.start();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
System.out.println("Main thread interrupted.");
}

lo.stop();
hi.stop();
// Wait for child threads to terminate.
try {
hi.t.join();
lo.t.join();
} catch (InterruptedException e) {
System.out.println("InterruptedException caught");
}
System.out.println("Low-priority thread: " + lo.click);
System.out.println("High-priority thread: " + hi.click);
}
}
The output of this program, shown as follows when run under Windows, indicates that
the threads did context switch, even though neither voluntarily yielded the CPU nor blocked
for I/O. The higher-priority thread got the majority of the CPU time.
Low-priority thread: 4408112
High-priority thread: 589626904

Deadlock
A special type of error that you need to avoid that relates specifically to multitasking is
deadlock, which occurs when two threads have a circular dependency on a pair of
synchronized
objects. For example, suppose one thread enters the monitor on object X and another thread
enters the monitor on object Y. If the thread in X tries to call any synchronized method on Y,
it will block as expected. However, if the thread in Y, in turn, tries to call any synchronized
method on X, the thread waits forever, because to access X, it would have to release its own
lock on Y so that the first thread could complete. Deadlock is a difficult error to debug for
two reasons:
• In general, it occurs only rarely, when the two threads time-slice in just the right way.
• It may involve more than two threads and two synchronized objects. (That is, deadlock
can occur through a more convoluted sequence of events than just described.)
To understand deadlock fully, it is useful to see it in action. The next example creates two
classes, A and B, with methods foo( ) and bar( ), respectively, which pause briefly before
trying to call a method in the other class. The main class, named Deadlock, creates an A
and a B instance, and then starts a second thread to set up the deadlock condition. The
foo( ) and bar( ) methods use sleep( ) as a way to force the deadlock condition to occur.

// An example of deadlock.
class A {
synchronized void foo(B b) {
String name = Thread.currentThread().getName();
System.out.println(name + " entered A.foo");
try {
Thread.sleep(1000);
} catch(Exception e) {
System.out.println("A Interrupted");
}
System.out.println(name + " trying to call B.last()");
b.last();
}
synchronized void last() {
System.out.println("Inside A.last");
}
}
class B {
synchronized void bar(A a) {
String name = Thread.currentThread().getName();
System.out.println(name + " entered B.bar");
try {
Thread.sleep(1000);
} catch(Exception e) {
System.out.println("B Interrupted");
}
System.out.println(name + " trying to call A.last()");
a.last();
}
synchronized void last() {
System.out.println("Inside A.last");
}
}
class Deadlock implements Runnable {
A a = new A();
B b = new B();
Deadlock() {
Thread.currentThread().setName("MainThread");
Thread t = new Thread(this, "RacingThread");
t.start();
a.foo(b); // get lock on a in this thread.
System.out.println("Back in main thread");
}
public void run() {
b.bar(a); // get lock on b in other thread.
System.out.println("Back in other thread");
}
public static void main(String args[]) {
new Deadlock();
}
}

When you run this program, you will see the output shown here:

MainThread entered A.foo


RacingThread entered B.bar
MainThread trying to call B.last()
RacingThread trying to call A.last()

You might also like