Multithreading: Concurrency in Java
Multithreading: Concurrency in Java
CONCURRENCY IN JAVA
04/05/2023 BY ELIAS J. 1
Introduction
The human body performs a great variety of operations in parallel (or concurrently).
Respiration, blood circulation, digestion, thinking and walking, for example, can occur
concurrently. Same is true for as can all the senses—sight, touch, smell, taste and hearing.
Computers, too, can perform operations concurrently. It’s common for personal computers to
compile a program, send a file to a printer and receive electronic mail messages over a network
concurrently (i.e. at the same time)
Only computers that have multiple processors can truly execute multiple instructions concurrently.
On the other hand, Operating systems on single-processor computers create the illusion of
concurrent execution by rapidly switching between activities but on such computers only a single
instruction can execute at once.
Today’s multicore computers have multiple processors that enable computers to perform tasks
truly concurrently.
04/05/2023 BY ELIAS J. 2
Java Concurrency
One of the powerful features of Java is its built-in support for multithreading—the concurrent
running of multiple tasks within a program (i.e. within single program).
In many programming languages, you have to invoke system-dependent procedures and functions to
implement multithreading.
Java makes concurrency available to you through the language and APIs.
Java programs can have multiple threads of execution, where each thread has its own method-call stack and
program counter, allowing it to execute concurrently with other threads while sharing with them
application-wide resources such as memory. This capability is called multithreading.
A problem with single-threaded applications that can lead to poor responsiveness is that
lengthy activities must complete before others can begin.
In a multithreaded application, threads can be distributed across multiple processors (if
available) so that multiple tasks execute concurrently and the application can operate
more efficiently.
Multithreading can also increase performance on single-processor systems that simulate
concurrency—when one thread cannot proceed (because, for example, it is waiting for
the result of an I/O operation), another can use the processor.
04/05/2023 BY ELIAS J. 3
Thread Concept
For example, when downloading a large file (e.g., an image, an audio clip or a video clip) over the
Internet, the user may not want to wait until the entire clip downloads before starting the
playback. To solve this problem, multiple threads can be used—one to download the clip, and
another to play it. These activities proceed concurrently. But what if, the downloading thread has
not downloaded any part of the file yet or if it downloaded very little amount which results in
choppy (or irregularly broken up) playback? To avoid such choppy playback, the threads are
synchronized (that is, their actions are coordinated) so that the player thread doesn’t begin until
there’s a sufficient amount of the clip in memory to keep the player thread busy (i.e. to satisfactory
playback).
The Java Virtual Machine (JVM) creates threads to run programs and threads to perform
housekeeping tasks such as garbage collection.
A program may consist of many tasks that can run concurrently. A thread is the flow of execution,
from beginning to end, of a task. It provides the mechanism for running a task. With Java, you can
launch multiple threads from a program concurrently. These threads can be executed
simultaneously in multiprocessor systems, as shown in Figure(a) or they can be executed
alternately in single-processor systems, as shown in Figure(b).
04/05/2023 BY ELIAS J. 4
Thread Concept….
In single-processor systems, as shown in Figure(b), the multiple threads share CPU time known as
time sharing, and the operating system is responsible for scheduling and allocating resources to
them. This arrangement is practical, because most of the time the CPU is idle. It does nothing, for
example, while waiting for the user to enter data.
Multithreading can make your program more responsive and interactive, as well as enhance
performance. For example, a good word processor lets you print or save a file while you are typing.
In some cases, multithreaded programs run faster than single-threaded programs even on single-
processor systems. Java provides exceptionally good support for creating and running threads and
for locking resources to prevent conflicts.
04/05/2023 BY ELIAS J. 5
Thread Concept….
In summary, thread is:
A thread is essentially an object that facilitates the execution of a task. It is the basic unit of program execution. A process
can have several threads running concurrently, each performing a different job.
Multithreading is the ability to have multiple “simultaneous” lines of execution running in a single program.
04/05/2023 BY ELIAS J. 6
Thread States: Life Cycle of a Thread
At any time, a thread is said to be in one of several thread states—illustrated in the UML state
diagram below:
04/05/2023 BY ELIAS J. 7
Thread States…
A. New and Runnable States:
A new thread begins its life cycle in the NEW state. It remains in this state until the program starts the thread,
which places it in the RUNNABLE state.
A thread in the runnable state is considered to be executing its task.
B. WAITING State
Sometimes a runnable thread transitions to the WAITING state while it waits for another thread to perform a task.
A waiting thread transitions back to the runnable state only when another thread notifies it to continue executing.
C. TIMED WAITING State
A runnable thread can enter the TIMED WAITING state for a specified interval of time. It transitions back to the
runnable state when that time interval expires or when the event it’s waiting for occurs.
TIMED WAITING and WAITING threads cannot use a processor, even if one is available.
A runnable thread can transition to the timed waiting state if it provides an optional wait interval when it’s waiting
for another thread to perform a task. Such a thread returns to the runnable state when it’s notified by another
thread or when the timed interval expires—whichever comes first.
Another way to place a thread in the timed waiting state is to put a runnable thread to SLEEP. A sleeping thread
remains in the timed waiting state for a designated period of time (called a sleep interval), after which it returns to
the runnable state. Threads sleep when they momentarily do not have work to perform.
04/05/2023 BY ELIAS J. 8
Thread States…
For example, a word processor may contain a thread that periodically backs up (i.e., saves or writes a copy of) the current
document to disk for recovery purposes. If the thread did not sleep between successive backups, it would require a loop
in which it continually tested whether it should write a copy of the document to disk. This loop would consume processor
time without performing productive work, thus reducing system performance. In this case, it’s more efficient for the
thread to specify a sleep interval (equal to the period between successive backups) and enter the timed waiting state. This
thread is returned to the runnable state when its sleep interval expires, at which point it writes a copy of the document to
disk and reenters the timed waiting state.
D. BLOCKED State
A runnable thread transitions to the BLOCKED state when it attempts to perform a task that cannot be
completed immediately and it must temporarily wait until that task completes.
For example, when a thread issues an input/output request, the operating system blocks the thread
from executing until that I/O request completes—at that point, the blocked thread transitions to the
runnable state, so it can resume execution.
A blocked thread cannot use a processor, even if one is available.
E. TERMINATED State
A runnable thread enters the terminated state (sometimes called the dead state) when it successfully
completes its task or otherwise terminates (perhaps due to an error).
04/05/2023 BY ELIAS J. 9
Thread States…
Operating-System View of the Runnable State
o At the operating system level, Java’s RUNNABLE STATE typically encompasses two separate states: Ready and Running States
o The operating system hides these states from the Java Virtual Machine (JVM), which sees only the runnable state.
o When a thread first transitions to the runnable state from the new state, it’s in the ready state. Then, a ready thread
enters the running state (i.e., begins executing) when the operating system assigns it to a processor—also known as
dispatching the thread.
o In most operating systems, each thread is given a small amount of processor time—called a quantum or timeslice—with
which to perform its task. Deciding how large the quantum should be is a key topic in operating systems courses.
o When its quantum expires, the thread returns to the ready state, and the operating system assigns another thread to the
processor.
o Transitions between the ready and running states are handled solely by the operating system. The JVM does not “see” the
transitions—it simply views the thread as being runnable and leaves it up to the operating system to transition the thread
between ready and running. The process that an operating system uses to determine which thread to dispatch is called
thread scheduling and is dependent on thread priorities.
04/05/2023 BY ELIAS J. 10
Lifecycle of a thread
04/05/2023 BY ELIAS J. 12
A. Extending “Thread” Class
When we wish to make a Java program use threading, the easiest way is to say our class extends
the class Thread.
E.g. Suppose we want to write a Java program which has two tasks. The first task is simply to display numbers from 1 to 5;
the second task is to display letters from ‘A’ to ‘E’. But we want these two tasks to run in different threads. Let’s say the
first task is going to be done by the main method (i.e. by the main thread). For the second task we have to create
additional thread.
To create our additional thread using the extending approach, do the following:
Step-1: Create your class and make it extend the “Thread” class.
E.g. class MyOwnThread extends Thread
Step-2: Override the “run()” method which our class inherited from “Thread” class
NB: The “Thread” class has a method called “run()” which by default is empty. This method is a method which
serves as a body of a thread. All of the work to be done by our new thread happens in this method. Hence, we
need to override this method so that we make it do any task we want. For this particular example, we want it to
display letters from ‘A’ to ‘E’.
Step-3: Create an object of Thread type.
E.g. MyOwnThread mt = new MyOwnThread(); // it is object of type Thread since we extended class “Thread”
E.g. Alternatively you can do this: Thread mt = new MyOwnThread(); //just same
Step-4: Start your thread object by calling “start()” method
E.g. mt.start();
04/05/2023 BY ELIAS J. 13
..
The following is the complete example:
Since both the threads have equal priority,
public class MyOwnThread extends Thread { //extending “Thread”
the output of the code can be anything:
public void run(){ //overriding the “run()” method
System.out.println("A"); Output Output Output Output Output
System.out.println("B");
System.out.println("C"); 1 1 1 A 1
System.out.println("D"); A 2 2 B A
System.out.println("E"); B 3 A 1 2
} C 4 B 2 B
public static void main(String[] args) { D 5 C C 3
MyOwnThread mt = new MyOwnThread(); //creating a Thread object E A D D C
mt.start(); //starting our letter printing thread 2 B E 3 4
3 C 3 4 D
System.out.println(1); 4 D 4 5 5
System.out.println(2); 5 E 5 E E
System.out.println(3);
System.out.println(4);
System.out.println(5);
}
04/05/2023 BY ELIAS J. 14
..
Two Schemes of starting a thread from a subclass:
1.1.The start() method is not in the constructor of the
subclass
– The start() method needs to be explicitly invoked after
object instance of the subclass is created in order to start
the thread
1.2.The start() method is in the constructor of the subclass
– Creating an object instance of the subclass will start the
thread
04/05/2023 BY ELIAS J. 15
B. Implementing “Runnable” Interface
To create our additional thread using the this approach, do the following:
Step-1: Create your class and make it implement the “Runnable” interface.
E.g. class MyOwnThread implements Runnable
Step-2: Implement the “run()” method of “Runnable” interface
NB: Again, “run()” method is a method which serves as a body of a thread. All of the work to be done by our
new thread happens in this method. Hence, we need to implement this method so that we make it do any task
we want. For this particular example, we want it to display letters from ‘A’ to ‘E’.
Step-3: Create an object of Runnable type.
E.g. MyOwnThread mt = new MyOwnThread(); // it is object of type Runnable since we implemented the interface “Runnable”
E.g. Alternatively you can do this: Runnable mt = new MyOwnThread(); //just same
Step-4: Create a “Thread” object by passing the above runnable object to its constructor.
E.g. Thread tr = new Thread(mt);
Step-5: Start your thread object by calling “start()” method
E.g. tr.start();
04/05/2023 BY ELIAS J. 16
..
The following is the complete example:
Same as in the previous example, since both
public class MyOwnThread implements Runnable { //implementing “Runnable”
public void run(){ //implementing the “run()” method
the threads have equal priority, the output
System.out.println("A"); of the code can be anything.
System.out.println("B");
Output Output Output Output Output
System.out.println("C");
System.out.println("D");
1 1 1 A 1
System.out.println("E");
A 2 2 B A
}
public static void main(String[] args) {
B 3 A 1 2
MyOwnThread mt = new MyOwnThread(); //creating a Runnable object
C A B 2 B
Thread tr = new Thread(mt); //creating a thread
D 4 C C 3
tr.start(); //starting our letter printing thread 2 B D D C
3 C E 3 4
System.out.println(1); 4 D 3 4 D
System.out.println(2); 5 E 4 5 5
System.out.println(3); E 5 5 E E
System.out.println(4);
System.out.println(5);
}
}
NB: Out of the two approaches of creating threads, the second one is preferred, i.e. “implementing Runnable interface” is preferred. WHY?
04/05/2023 BY ELIAS J. 17
Comparing the two approaches
Notice that both examples invoke Thread.start in order to start the new thread.
The first idiom is easier to use in simple applications, but is limited by the fact that your task class
must be a descendant of Thread.
This means that your class has already inherited from Thread class and cannot inherit from any other class
even if you want to. Multiple Inheritance not allowed in Java.
The second idiom, which employs a Runnable object, is more general, because the Runnable
object can subclass a class other than Thread.
This simply means that your class has a freedom to subclass any other class.
This approach also separates the Runnable task from the Thread object that executes the task. Not only is
this approach more flexible, but it is applicable to the high-level thread management APIs. So, this
approach Wins!!
04/05/2023 BY ELIAS J. 18
The Thread Class
The Thread class has many useful methods for thread management. Few of them are shown below:
«interface»
java.lang.Runnable
java.lang.Thread
+Thread() Creates a default thread.
+Thread(task: Runnable) Creates a thread for a specified task.
+start(): void Starts the thread that causes the run() method to be invoked by the JVM.
+isAlive(): boolean Tests whether the thread is currently running.
+setPriority(p: int): void Sets priority p (ranging from 1 to 10) for this thread.
+join(): void Waits for this thread to finish.
+sleep(millis: long): void Puts the runnable object to sleep for a specified time in milliseconds.
+yield(): void Causes this thread to temporarily pause and allow other threads to execute.
+interrupt(): void Interrupts this thread.
04/05/2023 BY ELIAS J. 19
More elaboration and methods
No. Method Description
1. public void start() Starts the thread in a separate path of execution, then invokes the run() method on this
Thread object.
2. public void getName() There is a method for retrieving the name. By default threads are named as “Thread-0”,
“Thread-1”, “Thread-2”, etc based on their object creation order. We can aslo give them
our own name using the method “setName”, described below.
3. public final void setName(String name) Changes the name of the Thread object.
4. public final void setPriority(int priority) Sets the priority of this Thread object. The possible values are between 1 and 10.
5. public final void join(long millisec) The current thread invokes this method on a second thread, causing the current thread to
block until the second thread terminates or the specified number of milliseconds passes.
6. public void interrupt() Interrupts this thread, causing it to continue execution if it was blocked for any reason.
7. public final boolean isAlive() Returns true if the thread is alive, which is any time after the thread has been started but
before it runs to completion.
04/05/2023 BY ELIAS J. 20
The Static yield() Method
The Thread class has many useful methods. You can use the yield() method to temporarily release time for other threads.
For example:
04/05/2023 BY ELIAS J. 21
The Static sleep(milliseconds) Method
The sleep(long mills) method puts the thread to sleep for the specified time in milliseconds. For example:
Every time a number printed, the thread is put to sleep for 1000 millisecond (equivalent to 1 second). This means, the for loop
displays the numbers in gap of 1 second.
NB: The “sleep” method throws a checked exception called InterruptedException
04/05/2023 BY ELIAS J. 22
The join() Method
You can use the join() method to force one thread to wait for another thread to finish. For example:
Here, supposing the ‘lastNum’ is 100, all the numbers after 50 are printed after thread ‘printA’ is finished. This is because, inside thread ‘print100’ we made a call to ‘join’ method (printA.join()),
this means our current thread ‘print100’ must wait until ‘printA’ finishes all of its tasks.
NB: the “join” method also throws a checked exception called InterruptedException
04/05/2023 BY ELIAS J. 23
The Static currentThread() Method
This method returns a reference to the currently executing thread object. In other words, if you want to know which thread
is currently executing, you execute this method…. and what would be the syntax to do that?
Thread.currentThread();
If you want the name of the current Thread instead of its reference, use the “getName” method:
Thread.currentThread().getName(); //This returns a string
04/05/2023 BY ELIAS J. 24
isAlive(), interrupt(), and isInterrupted()
The isAlive() method is used to find out the state of a thread. It returns true if a thread is in the Ready, Blocked, or Running
state; it returns false if a thread is new and has not started or if it is finished.
The interrupt() method interrupts a thread in the following way: If a thread is currently in the Ready or Running state, its
interrupted flag is set; if a thread is currently blocked, it is awakened and enters the Ready state, and an
java.io.InterruptedException is thrown.
04/05/2023 BY ELIAS J. 25
Thread Priority
Each thread is assigned a default priority of Thread.NORM_PRIORITY. You can reset the priority using setPriority(int priority).
Some constants for priorities include
Thread.MIN_PRIORITY
Thread.MAX_PRIORITY
Thread.NORM_PRIORITY
E.g. Thread tt = new MyThread();
tt.setPriority(Thread.MAX_PRIORITY);
By default, MIN_PRIORITY is 1 and MAX_PRIORITY is 10.
04/05/2023 BY ELIAS J. 26
Thread-Safe
Thread-safe denotes a system that can automatically handle potential conflicts among running
threads.
Imagine that many threads are running and each of which are accessing a certain method that
modifies a variable value.
At some point the variables value can unexpectedly be distorted.
Java was written from scratch with thread safety in mind, and every Java class in its library is
thread-safe.
However, you need to worry about the thread safety of your own classes.
04/05/2023 BY ELIAS J. 27
Keep in mind while using threads
What will happen if more than one thread calls into this method at the same time?
Do you need to protect it in some way?
What about your class as a whole?
Are you assuming that only one of its methods is running at a time?
04/05/2023 BY ELIAS J. 28
Thread Synchronization
A shared resource may be corrupted if it is accessed simultaneously by multiple threads. For example, two unsynchronized
threads accessing the same bank account may cause conflict.
1 0 newBalance = bank.getBalance() + 1;
2 0 newBalance = bank.getBalance() + 1;
3 1 bank.setBalance(newBalance);
4 1 bank.setBalance(newBalance);
04/05/2023 BY ELIAS J. 29
Thread Synchronization
For example,
let’s say we have a class called “Account” which has two methods: deposit, and getBalance.
Let’s say we have another class called “BankBranchThread”, inside this class we create and start four Threads all of which
use a common object called “acc”.
public class BankBranchThread extends Thread {
static Account acc = new Account(); //static because all the four threads must share this object
class Account { public void run() {
double balance = 0; acc.deposit(1); //calling the deposit method by passing amount 1birr as parameter
}
public void deposit(double amount) { public static void main(String[] args) {
double newBalance = balance + amount; BankBranchThread t1 = new BankBranchThread();
try { BankBranchThread t2 = new BankBranchThread();
Thread.sleep(500); BankBranchThread t3 = new BankBranchThread(); Four threads created
} catch (Exception e) { BankBranchThread t4 = new BankBranchThread();
}
balance = newBalance; t1.start(); t2.start(); t3.start(); t4.start(); All the four threads started
}
try {
public double getBal() { t1.join();
return balance; t3.join(); The joins are important because we want the ‘main’ thread to wait for all of the
} t2.join(); four threads before displaying the balance.
} t4.join();
} catch (Exception e) {
NB: The delay by sleep method, Thread.sleep(500), is }
deliberately added to magnify the data-corruption
problem and make it easy to see problems of System.out.println("Bal:" + acc.getBal());
unsynchronized shared resource access. }
}
04/05/2023 BY ELIAS J. 30
Race Condition
If you run the above code, the output we get from calling getBal method could be anything: It could be 1 or 2 or 3 or 4. But
naturally we expect the result to be just 4, because the four threads all added ‘1’ to the balance by calling the
acc.deposit(1). This is not the case though!
What, then, caused the error in the example? Here is a possible scenario:
1 0 newBalance = balance + 1;
2 0 newBalance = balance + 1;
3 1 balance = newBalance;
4 1 balance = newBalance;
);
The effect of this scenario is that Task 1 did nothing, because in Step 4 Task 2 overrides Task 1's result. Obviously, the
problem is that Task 1 and Task 2 are accessing a common resource in a way that causes conflict. This is a common problem
known as a race condition in multithreaded programs. A class is said to be thread-safe if an object of the class does not
cause a race condition in the presence of multiple threads. As demonstrated in the preceding example, the Account class is
not thread-safe.
04/05/2023 BY ELIAS J. 31
The synchronized keyword
To avoid race conditions, more than one thread must be prevented from simultaneously entering certain part of the
program, known as critical region. The critical region in the above example is the entire deposit method.
You can use the synchronized keyword to synchronize the method so that only one thread can access the method at a time.
There are several ways to correct the problem in the example, one approach is to make Account thread-safe by adding the
synchronized keyword in the header line of deposit method as follows:
A synchronized method acquires a lock before it executes. In the case of an instance method, the lock is on the object for
which the method was invoked. In the case of a static method, the lock is on the class.
If one thread invokes a synchronized instance method (respectively, static method) on an object, the lock of that object
(respectively, class) is acquired first, then the method is executed, and finally the lock is released. Another thread invoking
the same method of that object (respectively, class) is blocked until the lock is released.
04/05/2023 BY ELIAS J. 32
The synchronized keyword
With the deposit method synchronized, the preceding scenario cannot happen. If Task 1 enters the method, Task 2 is blocked until Task 1 finishes the
method, as shown in figure below:
04/05/2023 BY ELIAS J. 33
Synchronizing Statements
Invoking a synchronized instance method of an object acquires a lock on the object, and invoking a synchronized static method of a
class acquires a lock on the class.
A synchronized statement can be used to acquire a lock on any object, not just this object, when executing a block of the code in a
method. This block is referred to as a synchronized block. The general form of a synchronized statement is as follows:
synchronized (expr) {
statements;
}
The expression expr must evaluate to an object reference (i.e. it must be reference of an object we want to lock). If the object is
already locked by another thread, the thread is blocked until the lock is released. When a lock is obtained on the object, the
statements in the synchronized block are executed, and then the lock is released.
E.g. The following is another way of synchronizing the deposit method using synchronized statement (or synchronized block)
public void deposit(double amount) {
synchronized(this){ //’this’ is a reference to the Account object being used currently.
double newBalance = balance + amount;
try {
Thread.sleep(500);
} catch (Exception e) {
}
balance = newBalance;
}
}
04/05/2023 BY ELIAS J. 34
Synchronizing Statements
We can achieve the same result by using the synchronized block inside the ‘run’ method as follow:
public void run() {
synchronized(acc){
acc.deposit(1);
}
}
NB: the Account object being shared by the four threads is referenced by ‘acc’. Hence, it is the one to be synchronized. That
means, as long as one thread is working on that object by acquiring the lock, others shall be blocked when they try to access
it until the lock on it by thread one is released.
public synchronized void xMethod() { The method on the left is equivalent to:
// method body
public void xMethod() {
}
synchronized (this) {
// method body
}
}
04/05/2023 BY ELIAS J. 35
Deadlock
Sometimes two or more threads need to acquire the locks on several shared objects. This could cause deadlock, in which each thread has
the lock on one of the objects and is waiting for the lock on the other object. Consider the scenario with two threads and two objects, as
shown in Figure below. Thread 1 acquired a lock on object1 and Thread 2 acquired a lock on object2. Now Thread 1 is waiting for the lock
on object2 and Thread 2 for the lock on object1. The two threads wait for each other to release the in order to get the lock, and neither
can continue to run.
1 synchronized (object1) {
2 synchronized (object2) {
3 // do something here
4 // do something here
5 synchronized (object2) {
6 synchronized (object1) {
// do something here // do something here
} }
} }
Preventing Deadlock: Deadlock can be easily avoided by using a simple technique known as resource ordering. With this technique,
you assign an order on all the objects whose locks must be acquired and ensure that each thread acquires the locks in that order. For the
example in Figure above, suppose the objects are ordered as object1 and object2. Using the resource ordering technique, Thread 2 must
acquire a lock on object1 first, then on object2. Once Thread 1 acquired a lock on object1, Thread 2 has to wait for a lock on object1. So
Thread 1 will be able to acquire a lock on object2 and no deadlock would occur.
04/05/2023 BY ELIAS J. 36
Thread Control
Core Java provides a complete control over multithreaded program. You can develop a multithreaded program
which can be suspended, resumed or stopped completely based on your requirements. There are various static
methods which you can use on thread objects to control their behavior. Following table lists down those
methods:
No. Methods with Description
public void suspend()
1
This method puts a thread in suspended state and can be resumed using resume() method.
public void stop()
2
This method stops a thread completely.
public void resume()
3
This method resumes a thread which was suspended using suspend() method.
public void wait()
4
Causes the current thread to wait until another thread invokes the notify().
public void notify()
5
Wakes up a single thread that is waiting on this object's monitor.
04/05/2023 BY ELIAS J. 37