0% found this document useful (0 votes)
28 views

Chapter 1 Multi Threading

This document discusses multi-threading in Java, including thread states like new, runnable, blocked, waiting and terminated. It also covers topics like thread priority, scheduling, creating threads by extending Thread or implementing Runnable, and thread synchronization.

Uploaded by

robel damise
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
28 views

Chapter 1 Multi Threading

This document discusses multi-threading in Java, including thread states like new, runnable, blocked, waiting and terminated. It also covers topics like thread priority, scheduling, creating threads by extending Thread or implementing Runnable, and thread synchronization.

Uploaded by

robel damise
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 21

Samara University College of Engineering and Technology Department of Information Technology

Table of Contents
Chapter 1 ............................................................................................................................................................ 2
1. Multi-Threading ......................................................................................................................................... 2
1.1. Introduction ......................................................................................................................................... 2
1.2. Thread States: Life Cycle of a Thread ................................................................................................ 3
1.2.1. New Threads ................................................................................................................................ 3
1.2.2. Runnable Threads ........................................................................................................................ 3
1.2.3. Blocked and Waiting Threads ...................................................................................................... 3
1.2.4. Terminated Threads ..................................................................................................................... 4
1.3. Thread priority and thread scheduling ................................................................................................ 4
1.4. Creating and executing threads ........................................................................................................... 8
1.4.1. Extending Thread ......................................................................................................................... 8
1.4.2. Implementing Runnable ............................................................................................................... 9
1.5. Thread synchronization ..................................................................................................................... 11
1.5.1. Multithreading without synchronization .................................................................................... 17
1.5.2. Multithreading with synchronization ......................................................................................... 19

Advanced Programming Chapter 1 Multi-Threading by Bantamlak Dejene 1


Samara University College of Engineering and Technology Department of Information Technology

Chapter 1

1. Multi-Threading
1.1. Introduction

Unlike many other computer languages, Java provides built-in support for programming. Java provides built-
in support for multithreaded programming. Java is a multi-threaded programming language which means we
can develop multithreaded program using Java. A thread is a control/flow/path of execution that exists within
a process. A process may have one or more threads in it and is referred to as single-threaded or multi-threaded
process respectively. In a single-threaded process, there is only one flow to execution of instructions, whereas
a multithreaded process has multiple sets of instructions that are executed concurrently; it hence has multiple
concurrent flows/paths of execution. So, a multi-threaded process has two or more parts that can run
concurrently and each part can do a designated task at the same time. Multi-threading enables us to write
programs in a way where multiple activities can proceed concurrently in the same program. However, note
that a system having only one execution core executes multiple threads in an interleaved way resulting in no
extra benefit in terms of effective execution time.

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. Thus, 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, on the other hand, 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 the next is low cost. While Java
programs make use of process based multitasking environments, process-based multitasking is not under the
control of Java.

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. A multithreading is a specialized form
of multitasking. Multitasking threads require less overhead than multitasking processes. Another term related
to threads is: process: A process consists of the memory space allocated by the operating system that can
contain one or more threads. A thread cannot exist on its own; it must be a part of a process. A process remains
running until all of the non-daemon threads are done executing. Multithreading enables you to write very
efficient programs that make maximum use of the CPU, because idle time can be kept to a minimum. Thus,

Advanced Programming Chapter 1 Multi-Threading by Bantamlak Dejene 2


Samara University College of Engineering and Technology Department of Information Technology

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: process based and thread-based. It is important to understand the difference between the two.

1.2. Thread States: Life Cycle of a Thread

A thread goes through various stages in its life cycle. For example, a thread is born, started, runs, and then
dies.

1.2.1. New Threads

A new thread begins its life cycle in the new state. It remains in this state until the program starts the thread.
It is also referred to as a born thread. When you create a thread with the new operator—for example, new
Thread(r)—the thread is not yet running. This means that it is in the new state. When a thread is in the new
state, the program has not started executing code inside of it. A certain amount of bookkeeping needs to be
done before a thread can run.

1.2.2. Runnable Threads

After a newly born thread is started, the thread becomes runnable. A thread in this state is considered to be
executing its task. A runnable thread may or may not actually be running. It is up to the operating system to
give the thread time to run. (The Java specification does not call this a separate state, though. A running thread
is still in the runnable state.) Once a thread is running, it doesn’t necessarily keep running. In fact, it is desirable
that running threads occasionally pause so that other threads have a chance to run. The details of thread
scheduling depend on the services that the operating system provides. Preemptive scheduling systems give
each runnable thread a slice of time to perform its task. On a machine with multiple processors, each processor
can run a thread, and you can have multiple threads run in parallel. Of course, if there are more threads than
processors, the scheduler still has to do time slicing. Always keep in mind that a runnable thread may or may
not be running at any given time. (This is why the state is called “runnable” and not “running.”)

java.lang.Thread
static void yield() causes the currently executing thread to yield to another thread. Note that
this is a static method.

1.2.3. Blocked and Waiting Threads

Sometimes, a thread transitions to the waiting state while the thread waits for another thread to perform a task.
A thread transitions back to the runnable state only when another thread signals the waiting thread to continue
Advanced Programming Chapter 1 Multi-Threading by Bantamlak Dejene 3
Samara University College of Engineering and Technology Department of Information Technology

executing. When a thread is blocked or waiting, it is temporarily inactive. It doesn’t execute any code and
consumes minimal resources. It is up to the thread scheduler to reactivate it.

When a thread is blocked or waiting (or, of course, when it terminates), another thread will be scheduled to
run. When a thread is reactivated (for example, because its timeout has expired or it has succeeded in acquiring
a lock), the scheduler checks to see if it has a higher priority than the currently running threads. If so, it
preempts one of the current threads and picks a new thread to run. A runnable thread can enter the timed
waiting state for a specified interval of time. A thread in this state transitions back to the runnable state when
that time interval expires or when the event it is waiting for occurs.

1.2.4. Terminated Threads

A runnable thread enters the terminated state when it completes its task or otherwise terminates. A thread is
terminated for one of two reasons:

➢ It dies a natural death because the run method exits normally.


➢ It dies abruptly because an uncaught exception terminates the run method.

In particular, you can kill a thread by invoking its stop method. That method throws a ThreadDeath error
object that kills the thread. However, the stop method is deprecated, and you should never call it in your own
code.

java.lang.Thread
void join() waits for the specified thread to terminate.

void join(long millis) waits for the specified thread to die or for the specified number of milliseconds to pass.

Thread.State getState() gets the state of this thread: one of NEW, RUNNABLE, BLOCKED, WAITING,
TIMED_WAITING, or TERMINATED.

void stop() stops the thread. This method is deprecated.

void suspend() suspends this thread’s execution. This method is deprecated.

void resume() resumes this thread. This method is only valid

1.3. Thread priority and thread scheduling

Java assigns to each thread a priority that determines how that thread should be treated with respect to the
others. Thread priorities are integers that specify the relative priority of one thread to another. As an absolute
value, a priority is meaningless; a higher-priority thread doesn’t run any faster than a lower-priority thread if
it is the only thread running. Instead, a thread’s priority is used to decide when to switch from one running

Advanced Programming Chapter 1 Multi-Threading by Bantamlak Dejene 4


Samara University College of Engineering and Technology Department of Information Technology

thread to the next. This is called a context switch. The rules that determine when a context switch
takes place are simple:

• A thread can voluntarily relinquish control. This is done by explicitly yielding, sleeping, or blocking on
pending I/O. In this scenario, all other threads are examined, and the highest-priority thread that is ready to
run is given the CPU.

• A thread can be preempted by a higher-priority thread. In this case, a lower-priority thread that does not
yield the processor is simply preempted - no matter what it is doing - by a higher-priority thread. Basically, as
soon as a higher-priority thread wants to run, it does. This is called preemptive multitasking.

In cases where two threads with the same priority are competing for CPU cycles, the situation is a bit
complicated. For operating systems such as Windows, threads of equal priority are time-sliced automatically
in round-robin fashion. For other types of operating systems, threads of equal priority must voluntarily yield
control to their peers. If they don’t, the other threads will not run.

Every Java thread has a priority that helps the operating system determine the order in which threads are
scheduled. Java thread priorities are in the range between MIN_PRIORITY (a constant of 1) and
MAX_PRIORITY (a constant of 10). By default, every thread is given priority NORM_PRIORITY (a
constant of 5). Threads with higher priority are more important to a program and should be allocated processor
time before lower-priority threads. However, thread priorities cannot guarantee the order in which threads
execute and are very much platform dependent.

In Java, every thread has a priority. Higher priority threads get more preference in terms of CPU, I/O time,
etc. than lower priority threads. However, how threads with different priorities should be handled depends
absolutely on the underlying platform specifically on its scheduling algorithm. Conceptually, threads of equal
priority should get equal chance. Similarly, higher priority threads should ideally receive more importance
than lower priority ones. Priorities are represented by integer numbers from 1 (lowest) to 10 (highest) which
are represented by two static final fields MIN_PRIORITY and MAX_PRIORITY of Thread class respectively.
A new thread receives its initial priority equal to the priority of its creator thread. The JVM assigns a priority
value equal to the final field NORM_PRIORITY to the main thread. Java defines these fields as follows:

public final static int MIN_PRIORITY = 1;


public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;

The following program prints their values:

public class MinMaxPriority {

Advanced Programming Chapter 1 Multi-Threading by Bantamlak Dejene 5


Samara University College of Engineering and Technology Department of Information Technology

public static void main(String args[]) {


System.out.println("Lowest thread priority: "+Thread.MIN_PRIORITY);
System.out.println("Normal thread priority: "+Thread.NORM_PRIORITY);
System.out.println("Highest thread priority: "+Thread.MAX_PRIORITY);
}
}

It generates the following output:

Lowest thread priority: 1


Normal thread priority: 5
Highest thread priority: 10

The following methods are available to work with priority:

public final int getPriority()


public final void setPriority(int newPriority)

The former returns the current priority of the thread, whereas the latter assigns the priority specified to the
thread. The following program demonstrates their usage:

public class PriorityDemo extends Thread {


public void run() {
System.out.println("Child's initial priority: "+getPriority());
setPriority(3);
System.out.println("After change, child's priority: "+getPriority());
}
public static void main(String args[]) {
Thread t = Thread.currentThread();
System.out.println("Main's initial priority: "+t.getPriority());
t.setPriority(7);
System.out.println("After change, main's priority: "+t.getPriority());
new PriorityDemo().start();
}
}

It results in the following output:

Advanced Programming Chapter 1 Multi-Threading by Bantamlak Dejene 6


Samara University College of Engineering and Technology Department of Information Technology

Main's initial priority: 5


After change, main's priority: 7
Child's initial priority: 7
After change, child's priority: 3

Note that a priority merely tells how important a thread should get with respect to others. Programs with
multiple threads, specifically with different priorities, may behave differently at different platforms especially
on preemptive and non-preemptive ones. The following program demonstrates how two threads with different
priorities are handled.

class MyThread extends Thread {


int count = 0;
public int getCount() { return count; }
public void run() {
while(true) count++;
}
}
public class PriorityTest {
public static void main(String args[]) throws InterruptedException {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();
Thread.sleep(100);
System.out.println("Thread 1 count: "+t1.getCount());
System.out.println("Thread 2 count: "+t2.getCount());
}
}

The main thread creates and starts two threads; one having highest priority and the other one having lowest
priority. The two threads continuously increment their respective local variable count. The main thread waits
for 100 milliseconds and prints the current values of the local count variable of the two threads. A sample
output of this program is shown when it was run under Windows 7.

Thread 1 count: 43802164

Advanced Programming Chapter 1 Multi-Threading by Bantamlak Dejene 7


Samara University College of Engineering and Technology Department of Information Technology

Thread 2 count: 40580567

Note that the actual values may vary in different platforms and of course at different times. Since, the two
children thread do not finish, terminate the program by pressing Ctrl-C.

1.4. Creating and executing threads

There are two ways to create threads: by extending the java.lang.Thread class and implementing
the java.lang.Runnable interface.

1.4.1. Extending Thread

One way to create threads is to write a class extending the Thread class and overriding its run() method as
follows:

public class SimpleThread extends Thread {


public void run() {
for(int i = 0; i < 4; i++)
System.out.println("In MyThread: "+i);
}
//...
}

The run() method provides the entry point for the thread. It contains codes to be executed concurrently with
other threads. Our run() method simply prints a message five times. The objects of this class are called threads.
The following creates one thread object: SimpleThread st = new SimpleThread();

When, this thread starts, the codes in the run() method get executed concurrently with other codes. Note that
in a uni-processor system, the codes run in interleaved way. Anyway, this thread does not start automatically.
It is started using its inherited start() method as follows: st.start(); When this statement is encountered, JVM
creates a new control of execution and associates the run() method with it and returns to the caller immediately.
The two threads, the current thread and the newly created one are then run concurrently. The current thread
executes statements after start() method and new thread executes the statements in run() method. Here is the
complete source code:

public class SimpleThread extends Thread {


public void run() {

Advanced Programming Chapter 1 Multi-Threading by Bantamlak Dejene 8


Samara University College of Engineering and Technology Department of Information Technology

for(int i = 0; i < 4; i++)


System.out.println("In MyThread: "+i);
}
public static void main(String args[]) {
SimpleThread st = new SimpleThread();
st.start();
for(int i = 0; i < 6; i++)
System.out.println("In main thread: "+i);
}
}

Here, the main thread creates a new thread and calls its start() method which spawns a new control of execution
and returns to the main thread immediately. The main thread then executes the statements after the start()
method whereas the new thread executes the run() method concurrently.

A sample output is shown below:

In main thread: 0
In MyThread: 0
In MyThread: 1
In main thread: 1
In main thread: 2
In MyThread: 2
In main thread: 3
In MyThread: 3
In main thread: 4
In main thread: 5

Note that the output may vary next time you run the program. This happens because the operating system may
use a different interleaving pattern depending on its scheduling policy and other parameters.

1.4.2. Implementing Runnable

Although, we can create threads extending the Thread class, it has an inherent problem. Since, Java does not
support multiple inheritance for classes, if a class has to extend another class, it cannot extend Thread.
Fortunately, Java provides an alternative way to create threads where we create a class implementing the
Runnable interface as follows:

Advanced Programming Chapter 1 Multi-Threading by Bantamlak Dejene 9


Samara University College of Engineering and Technology Department of Information Technology

public class MyClass implements Runnable {


public void run() {
for(int i = 0; i < 4; i++)
System.out.println("In MyThread: "+i);
}
//...
}

The class implements the run() method of Runnable interface. Note that an object of this class is not a thread;
it is merely runnable in the sense that its run(), method can be executed concurrently with other codes. The
thread object is created using any of the following constructors of Thread class:

public Thread(Runnable target)


public Thread(Runnable target, String name)

Both take a runnable object as first argument and create a thread object. So, we use the former one passing a
runnable object as follows: Thread t = new Thread(new MyClass());

This is an ordinary thread and can be started as usual: t.start();

It eventually calls runnable object’s run() method. Here is the complete source code using Runnable interface:

public class MyClass implements Runnable {


public void run() {
for(int i = 0; i < 4; i++)
System.out.println("In MyThread: "+i);
}
public static void main(String args[]) {
Thread t = new Thread(new MyClass());
t.start();
for(int i = 0; i < 6; i++)
System.out.println("In main thread: "+i);
}
}

This generates a similar output as shown below:

In main thread: 0

Advanced Programming Chapter 1 Multi-Threading by Bantamlak Dejene 10


Samara University College of Engineering and Technology Department of Information Technology

In MyThread: 0
In main thread: 1
In MyThread: 1
In main thread: 2
In MyThread: 2
In main thread: 3
In MyThread: 3
In main thread: 4
In main thread: 5

Again note that the output may vary every time you run the program.

1.5. Thread synchronization

There are many situations where multiple threads access common objects. When two or more threads access
the same object, the state of the object is not guaranteed to be correct all the time if special care is not taken.
To understand how it can happen, consider the following simple class:

class Counter {
int val = 0;
void inc() { val++; }
void dcr() { val--; }
}

The class Counter has a single field val (initialized to 0) that gets incremented and decremented by two
methods inc() and dcr() respectively. What should be the value of val if these two methods on a Counter object
are invoked same number of times? Obviously, the value should remain 0. However, that may not be the case
in a multithreading environment. To illustrate this, see the following program:

public class RaceConditionDemo extends Thread {


Counter c;
RaceConditionDemo(Counter c) { this.c = c; start(); }
public void run() {
for(int i=0;i<10000;i++) c.dcr();
}
public static void main(String args[]) throws Exception {
Counter c = new Counter();

Advanced Programming Chapter 1 Multi-Threading by Bantamlak Dejene 11


Samara University College of Engineering and Technology Department of Information Technology

RaceConditionDemo rc = new RaceConditionDemo(c);


for(int i=0;i<10000;i++) c.inc();
rc.join();
System.out.println("Final value of c.val: "+c.val);
}
}

Here, the main thread creates a Counter object c and a child thread with this object. The main thread and the
child thread then concurrently invoke inc() and dcr() methods on c 1000 times respectively. At end, main
thread prints the final value of c.val. When we executed this program 5 times, we got these values: -502, -394,
240, 505 and -664. What is wrong in the above program? Why it is not giving the correct result?
This happens since two non-atomic operations val++ and val-- are executed in overlapped way. Note that
these are high level instructions. Although, they look simple, JVM translates them into multiple steps. For
example, val++ will probably be translated into the following steps:

A. Get value of val


B. Increment this value by 1
C. Put it back to val

Similarly, JVM will probably translate val-- into following:

D. Get value of val


E. Decrement this value by 1
F. Put it back to val

If these two sets of instructions are executed concurrently, they may be interleaved as A B D E C F. If this
happens, val will have wrong final value -1. If last two instructions are interchanged, the value will again have
wrong value 1. Like these two, there are many other such interleaving patterns for which val will have incorrect
value. This situation is popularly known as race condition, and the segment of code where resources are shared
by multiple threads is known ascritical section. For a class like Counter, this incorrect result might not be too
harmful. However, consider the following class:

class Account {
int balance = 0;
void inc() { val++; }
void dcr() { val--; }
}

Advanced Programming Chapter 1 Multi-Threading by Bantamlak Dejene 12


Samara University College of Engineering and Technology Department of Information Technology

Here, even a small deviation from the correct balance is not acceptable. So, we must have some mechanism
to avoid this inconsistency. In general, we can avoid problem, if we can ensure that critical sections are not
executed in interleaved fashion. Fortunately, Java allows us to specify this using the keyword synchronized.
Two versions exist: synchronized methods and synchronized blocks. A method is made synchronized by
adding a synchronized keyword before its declaration:

class X{
synchronized void f() {
//...
}
}

Let us briefly understand what happens when a method is declared as synchronized. In Java, every object has
an internal entity called internal lock or intrinsic lock or monitor lock or simply lock or monitor. It is like a
privilege that only one thread can "own" at any one time. It can also be thought of as a room that one thread
can “occupy” at any one time. So, if a thread does own a lock of an object, no other thread can own the lock
of the same object until the thread that owns the lock releases it. Addition of a synchronized keyword means
just requesting to own a lock. When a thread invokes a non-static synchronized method on an object, it makes
a request to own the lock of the associated object. If the lock is not yet owned by some thread else, the
requesting thread is given the lock. Otherwise, it must wait until the lock is available. However, it must contend
with other waiting threads to own the lock. The lock is released when the owner quits the method. So, a
synchronized keyword before a method essentially tells the JVM

✓ not to interleave multiple parallel invocations of the method on the same object.
✓ to ensure that when one thread is executing a synchronized method on an object, all other threads wait
before invoking other synchronized methods on the same object.

With this understanding, we can rewrite our Counter class as follows:

class Counter {
int val = 0;
synchronized void inc() { val++; }
synchronized void dcr() { val--; }
}

Both the methods have been declared with synchronized keyword. Let us now understand how two threads
access a Counter object without interfering. When main thread executes, inc() on c, it owns c’s lock. Before
finishing this method, the child thread cannot own the lock of c; therefore cannot execute dcr(). Similarly,

Advanced Programming Chapter 1 Multi-Threading by Bantamlak Dejene 13


Samara University College of Engineering and Technology Department of Information Technology

when child thread executes dcr(), it owns the lock of c. During that time main thread waits. So, these methods
are executed without any form of interleaving. However, we have no control whatsoever on the order these
methods are executed. Needless to say that the program will give the correct result this time. What would you
do if you wanted to synchronize a shorter block of code instead of an entire method? Java has the other option
called synchronized block that takes the following form:

synchronized(o) {
//code to be synchronized
}

Here, o is some object that provides the lock. Our Counter class may be written as:

class Counter {
int val = 0;
Object o = new Object();
void inc() {
//other codes
synchronized(o) {
val++;
}
//other codes
}
void dcr() {
//other codes
synchronized(o) {
val--;
}
//other codes
}
}

Note that a synchronized method is a specific case of synchronized block where the lock is obtained from the
concerned object.

synchronized void f() {


//some code
}

Advanced Programming Chapter 1 Multi-Threading by Bantamlak Dejene 14


Samara University College of Engineering and Technology Department of Information Technology

The above declaration is equivalent to

void f() {
synchronized(this) {
//some code
}
}

It is also possible to make a static method synchronized as follows:

class X
synchronized static void g() { /*body*/ }
}

However, what happens if it is invoked since a static method is not associated with an object? In this case, the
Class object associated with the method’s class provides the lock. So, it is equivalent to

class X {
static void g() {
synchronized(X.class) { /*body*/ }
}
}

Since, access to static and non-static methods are handled by different locks and since a static field may be
accessed by both non-static and static methods, care should be taken when using both. Consider a slightly
modified version of our Counter class:

class Counter {
static int val = 0;
synchronized void inc() { val++; }
synchronized static void dcr() { val--; }
}

Here, the static field is incremented and decremented by a synchronized non-static and a synchronized static
method respectively. However, if they are invoked on a Counter object c, inc() uses the c’s lock whereas dcr()
uses lock of Counter’s Class object. This means that the methods may get interleaved raising an incorrect
result again. The following is a potential solution using synchronized block:

class Counter {

Advanced Programming Chapter 1 Multi-Threading by Bantamlak Dejene 15


Samara University College of Engineering and Technology Department of Information Technology

static int val = 0;


void inc() {
synchronized(Counter.class) {
val++;
}
}
static void dcr() {
synchronized(Counter.class) {
val--;
}
}
}

Here, both val++ and val-- statements are synchronized using the same lock obtained from Counter’s Class
object. Synchronized blocks sometimes may make programs more efficient than synchronized methods.
Consider the following class declaration:

class X {
int i1, i2;
void inc1() { i1++; }
void inc2() { i2++; }
}

Suppose that multiple threads access the method inc1() and also inc2(). So, fields may have incorrect values.
Here is a solution using synchronized methods:

class X {
int i1, i2;
synchronized void inc1() { i1++; }
synchronized void inc2() { i2++; }
}

However, since we have used synchronized methods, execution of inc1() unnecessarily blocks inc2() and vice
versa that reduces amount of concurrency. A more efficient solution using synchronized block may be written
as follows:

class X {
int i1, i2;

Advanced Programming Chapter 1 Multi-Threading by Bantamlak Dejene 16


Samara University College of Engineering and Technology Department of Information Technology

Object o1 = new Object(), o2 = new Object();


void inc1() {
synchronized(o1) {
i1++;
}
}
void inc2() {
synchronized(o2) {
i2++;
}
}
}

Here, multiple concurrent execution of inc1() and inc2() will work properly; at the same time one will not
block the other.

1.5.1. Multithreading without synchronization

Here is a simple example which may or may not print counter value in sequence and every time we run it, it
produces a different result based on CPU availability to a thread.

class PrintDemo {
public void printCount(){
try {
for(int i = 5; i > 0; i--) {
System.out.println("Counter --- " + i );
}
} catch (Exception e) {
System.out.println("Thread interrupted.");
}
}
}
class ThreadDemo extends Thread {
private Thread t;
private String threadName;
PrintDemo PD;
ThreadDemo( String name, PrintDemo pd){

Advanced Programming Chapter 1 Multi-Threading by Bantamlak Dejene 17


Samara University College of Engineering and Technology Department of Information Technology

threadName = name;
PD = pd;
}
public void run() {
PD.printCount();
System.out.println("Thread " + threadName + " exiting.");
}
public void start ()
{
System.out.println("Starting " + threadName );
if (t == null)
{
t = new Thread (this, threadName);
t.start ();
}
}
}
public class TestThread {
public static void main(String args[]) {
PrintDemo PD = new PrintDemo();
ThreadDemo T1 = new ThreadDemo( "Thread - 1 ", PD );
ThreadDemo T2 = new ThreadDemo( "Thread - 2 ", PD );
T1.start();
T2.start();
// wait for threads to end
try {
T1.join();
T2.join();
} catch( Exception e) {
System.out.println("Interrupted");
}
}
}

This produces a different result every time you run this program:

Advanced Programming Chapter 1 Multi-Threading by Bantamlak Dejene 18


Samara University College of Engineering and Technology Department of Information Technology

Starting Thread – 1
Starting Thread – 2
Counter --- 5
Counter --- 4
Counter --- 3
Counter --- 5
Counter --- 2
Counter --- 1
Counter --- 4
Thread Thread - 1 exiting.
Counter --- 3
Counter --- 2
Counter --- 1
Thread Thread - 2 exiting.

1.5.2. Multithreading with synchronization

Here is the same example which prints counter value in sequence and every time we run it, it produces the
same result.

class PrintDemo {
public void printCount(){
try {
for(int i = 5; i > 0; i--) {
System.out.println("Counter --- " + i );
}
} catch (Exception e) {
System.out.println("Thread interrupted.");
}
}
}
class ThreadDemo extends Thread {
private Thread t;
private String threadName;
PrintDemo PD;
ThreadDemo( String name, PrintDemo pd){

Advanced Programming Chapter 1 Multi-Threading by Bantamlak Dejene 19


Samara University College of Engineering and Technology Department of Information Technology

threadName = name;
PD = pd;
}
public void run() {
synchronized(PD) {
PD.printCount();
}
System.out.println("Thread " + threadName + " exiting.");
}
public void start ()
{
System.out.println("Starting " + threadName );
if (t == null)
{
t = new Thread (this, threadName);
t.start ();
}
}
}
public class TestThread {
public static void main(String args[]) {
PrintDemo PD = new PrintDemo();
ThreadDemo T1 = new ThreadDemo( "Thread - 1 ", PD );
ThreadDemo T2 = new ThreadDemo( "Thread - 2 ", PD );
T1.start();
T2.start();
// wait for threads to end
try {
T1.join();
T2.join();
} catch( Exception e) {
System.out.println("Interrupted");
}
}
}

Advanced Programming Chapter 1 Multi-Threading by Bantamlak Dejene 20


Samara University College of Engineering and Technology Department of Information Technology

This produces the same result every time you run this program:

Starting Thread – 1
Starting Thread – 2
Counter --- 5
Counter --- 4
Counter --- 3
Counter --- 2
Counter --- 1
Thread Thread - 1 exiting.
Counter --- 5
Counter --- 4
Counter --- 3
Counter --- 2
Counter --- 1
Thread Thread - 2 exiting.

Advanced Programming Chapter 1 Multi-Threading by Bantamlak Dejene 21

You might also like