Multithreading

Dated : 05 - 07 - 01

 

Multithreading

Java provides built-in support for Multithreaded programming.

A multithreaded program contains 2 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.

Specialized form of multitasking.

Two Distinct types of multitasking

Process-based

Thread-based

A process is, in essence, a program that is executing. Thus process-based multitasking is the feature that allows your computer to run two or more threads concurrently. Example: Process-based multitasking enables you to run the Java compiler at the same time that you are using a text editor.

A program is the smallest unit of code that can be dispatched by the scheduler.

In 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. Example: A Text Editor can format text at the same time it is printing, as long as these two actions are performed by two separate threads.

**Process-based multitasking deals with the "big picture", and thread-based multitasking handles the details.

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 are lightweight. They share the same address space and cooperatively share the same heavyweight process. Interthread communication is inexpensive, and context switching from one thread to next is less cost. While Java programs make use of process-based multitasking environments, process-based multitasking is not under the control of Java.

The Java Thread Model

Java uses threads to enable the entire environment to be asynchronous. This helps reduce inefficiency by preventing the waste of CPU cycles.

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 the 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 program dominating the system and preventing any other events from being processed. In single-threaded environment, when a thread blocks 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.

Threads can be in several states like running, ready, suspended, blocked, resumed.

Thread priorities

Java assigns to each thread a priority that determines how that thread should be treated with respect to others. Priorities are integer values.

A higher-priority thread doesnít run any faster than a low-priority thread if it is the only thread running. Instead, a priority is used to determine when to switch from one running thread to the next. This is called context-switch.

A thread can voluntarily relinquish control (yielding, sleeping or blocking on pending I/O)

A higher-priority thread (Preemptive multitasking) can preempt another thread

Synchronization

Example: If you want two threads to communicate and share a complicated data structure, such as a linked list, you need some way to ensure that they donít conflict with each other. That is, you must prevent one thread from writing data while the other thread is in the middle of reading it.

Java implements an elegant twist on an old-age model of interprocess synchronization: monitor. You can think of a monitor as a very small box that can hold only one thread. Once a thread enters a monitor, all other threads must wait until that thread exits the monitor. In this way, monitor can be used to protect a shared asset from being manipulated by more than one thread at a time.


There is no class "Monitor". Instead each object has its own implicit monitor that is automatically entered when one of the objectís synchronized method is called. Once a thread is inside a synchronized method, no other thread can call any other synchronized method on the same object. This enables you to write very clear and concise multithreaded code, because synchronization support is built into the language.

Messaging

Java provides a clean, low-cost way for 2 or more threads to talk to each other, via calls to predefined methods that all objects have. Javaís messaging system allows a thread to enter a synchronized method on an object, and then wait until some other thread explicitly notifies it to come out.

The Thread Class and the Runnable Interface

Javaís multithreading system is built upon the Thread class, its methods, and its companion interface, Runnable.

Thread encapsulates a thread of execution. Since you cant directly refer to the ethereal state of a running thread, you will deal with it through itís proxy, the Thread instance that spawned it.

To create a new thread your program will either extend Thread class or implement the Runnable interface.

Several methods of Thread Class:

getName Obtain a threadís name

getPriority Obtain a threadís priority

isAlive Determine if a thread is still running

join Wait for a thread to terminate

run Entry point for the thread

sleep Suspend a thread for a period of time

start Start a thread by calling itís run method

The Main Thread

When a java program starts up, one thread begins running immediately.

This main thread is important for 2 reasons:

1: It is the thread from which other "child" threads will be spawned.

2: It must be the last thread to finish execution. When the main thread stops, your program terminates.

Can be controlled through a thread object

static Thread currentThread()

static void sleep(long milliseconds) throws InterruptedException

static void sleep(long milliseconds, int nanoseconds) throws InterruptedException

final void setName(String threadName)

final String getName()

 

Sample Code

class CurrentThreadDemo {

public static void main (String args [ ]) {

Thread t = Thread.currentThread();

System.out.println ("Current Thread:" + t);

t.setName("My Thread");

System.out.println ("After name change:" + t);

try {

for (int n = 5; n > 0; n--) {

System.out.println (n);

Thread.sleep(1000);

}

}catch (InterruptedException ie) {

System.out.println ("Main Thread Interrupted");

}

}

}

 

Output:

Current Thread: Thread[ main,5,main]

After name change: Thread[My Thread,5,main]

5

4

3

2

1

name of the thread,itís priority, and the name of its group.

By default the name of the main thread is main. Itís priority is 5. And main is also the name of the group of threads to which this thread belongs.

A thread group is a data structure that controls the state of a collection of threads as a whole.

  

Creating a thread

Java defines two ways in which this can be accomplished:

Implement Runnable interface

Extend Thread class

Implementing Runnable

To implement Runnable, a class need only implement method called run(), which is declared like this:

public void run()

Inside run() you define the code that constitutes the new thread. Thread will end when run() returns.

Thread(Runnable threadOb, String threadName)

ThreadOb is an instance of a class that implements the Runnable interface. This defines where the execution of the thread will begin.

Sample Code

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(int i = 5; i > 0;i--) {

System.out.println("Child Thread:" + i);

Thread.sleep(500);

}

}catch (InterruptedException e) {

System.out.println("Child Interrupted");

}

System.out.println("Exiting Child Thread.");

}

}

class ThreadDemo {

public static void main (String args [ ]) {

new NewThread();

try {

for(int 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("Exiting Main Thread.");

}

}

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

Exiting Main Thread.

 

Extending Thread

Preceding program rewritten to extend Thread.

Sample Code

class NewThread extends Thread {

NewThread(){

super("Demo Thread");

System.out.println("Child Thread:" + this);

start();

}

  public void run () {

try {

for(int i = 5; i > 0;i--) {

System.out.println("Child Thread:" + i);

Thread.sleep(500);

}

}catch (InterruptedException e) {

System.out.println("Child Interrupted");

}

System.out.println("Exiting Child Thread.");

}

}

class ExtendThread {

public static void main (String args [ ]) {

new NewThread();

try {

for(int 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("Exiting Main Thread.");

}

}

 

Creating Multiple Threads

Sample Code

class NewThread implements Runnable {

String name;

Thread t;

NewThread(String threadname){

name = threadname;

t = new Thread(this, name);

System.out.println("New Thread:" + t);

t.start();

}

 

public void run () {

try {

for(int i = 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");

}

}

 

class MultiThreadDemo {

public static void main (String args [ ]) {

new NewThread("One");

new NewThread("Two");

new NewThread("Three");

try {

Thread.sleep(10000);

}catch (InterruptedException e) {

System.out.println("Main Thread Interrupted");

}

System.out.println("Main Thread exiting.");

}

}

Output

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

Two: 3

Three: 3

One: 2

Two: 2

Three: 2

One: 1

Two: 1

Three: 1

One exiting

Two exiting

Three exiting

Main Thread exiting.

 

Using isAlive() and join()

final boolean isAlive()

returns true if the thread upon which it is called is still running, returns false otherwise.

final void join() throws InterruptedException

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 allow you to specify maximum amount of time that you want to wait for the specified thread to terminate.

Sample Code

class NewThread implements Runnable {

String name;

Thread t;

NewThread(String threadname){

name = threadname;

t = new Thread(this, name);

System.out.println("New Thread:" + t);

t.start();

}

 

public void run () {

try {

for(int i = 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.");

}

}

class DemoJoin {

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());

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("Exiting Main Thread.");

}

}

Output:

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.

Thread Priorities

Used by the thread scheduler to decide when each thread should be allowed to run.

final void setPriority(int level)

fianl int getPriority()

The value of level must be within the range MIN_PRIORITY and MAX_PRIORITY. Currently these values are 1 and 10 respectively. Default priority is NORM_PRIORITY, which is currently 5. These are defined as final variables within Thread.

class Clicker implements Runnable {

int 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();

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 Windows98, indicates that the threads did context switch even though neither voluntarily yielded the CPU nor blocked for I/O. The higher priority thread got approx. 90% of the CPU time.

Low-Priority Thread:4408112

High-Priority Thread:589626904

The output depends on the speed of your CPU and the number of other tasks running in the system.

Volatile ensures that the value of running is examined each time the following loop iterates:

while(running) {

click++;

}

Synchronization

Key of synchronization is the concept of monitor (also called a semaphore). A monitor object is an object that is used as a mutually exclusive lock, or mutex. Only one thread can enter the monitor. All other threads attempting to enter the locked monitor will be suspended until the first exits the monitor.

These other threads are said to be waiting for the monitor.

A thread that owns a monitor can reenter the same monitor if it so desires.

Using Synchronized Method

 

// This program is not synchronized

class Callme {

void call(String msg) {

System.out.print("[" + msg);

try {

Thread.sleep(1000);

}catch (InterruptedException e) {

System.out.println("Interrupted");

}

System.out.print("]");

}

}

class Caller implements Runnable {

String msg;

Callme target;

Thread t;

public Caller (Callme targ, 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");

try {

ob1.t.join();

ob2.t.join();

ob3.t.join();

}catch (InterruptedException e) {

System.out.println("Interrupted");

}

}

}

Output:

[Hello[Synchronized[World]

]

]

 

This results in the mix-up output of the three message strings. In this program, nothing exists to stop all three threads from calling the same method, on the same object, at the same time. This is known as the Race Condition, because the three threads are racing each other to complete the method.

**Once a thread enters any synchronized method on an instance, no other thread can enter any other synchronized method on the same instance. However, nonsynchronized methods on that instance will continue to be callable.

// This program is synchronized

class Callme {

synchronized void call(String msg) {

System.out.print("[" + msg);

try {

Thread.sleep(1000);

}catch (InterruptedException e) {

System.out.println("Interrupted");

}

System.out.print("]");

}

}

class Caller implements Runnable {

String msg;

Callme target;

Thread t;

public Caller (Callme targ, 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");

try {

ob1.t.join();

ob2.t.join();

ob3.t.join();

}catch (InterruptedException e) {

System.out.println("Interrupted");

}

}

}

The synchronized statement

Imagine that you want to synchronize access to objects of a class that is not designed for multithreaded access. That is, the class does not use synchronized methods and you donít have access to the source code.

You can put calls to the methods defined by this class inside a synchronized block.

synchronized (object){

// statements to be synchronized

}

object is a reference to the object being synchronized.

// This program uses a synchronized block

class Callme {

void call(String msg) {

System.out.print("[" + msg);

try {

Thread.sleep(1000);

}catch (InterruptedException e) {

System.out.println("Interrupted");

}

System.out.print("]");

}

}

class Caller implements Runnable {

String msg;

Callme target;

Thread t;

public Caller (Callme targ, String s) {

target = targ;

msg = s;

t = new Thread(this);

t.start();

}

public void run () {

synchronized (target) {

target.call(msg);

}

}

}

class Synch1 {

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");

try {

ob1.t.join();

ob2.t.join();

ob3.t.join();

}catch (InterruptedException e) {

System.out.println("Interrupted");

}

}

}