Java Threads
Contents
- 1 Life Cycle of a Thread
- 1.1 Thread States
- 1.2 Thread Priorities
- 1.3 Runnable Interface
- 1.4 Thread Class
- 1.5 Thread Methods
- 1.5.1 public void start()
- 1.5.2 public void run()
- 1.5.3 public final void setName(String name)
- 1.5.4 public final void setPriority(int priority)
- 1.5.5 public final void setDaemon(boolean on)
- 1.5.6 public final void join(long millisec) - t1.join(2000);//start second thread after waiting for 2 seconds or if it's dead
- 1.5.7 public void interrupt()
- 1.5.8 public final boolean isAlive() - It tests if this thread is alive
- 1.5.9 public static void yield() -> RUNNING to RUNNABLE
- 1.5.10 public static void sleep(long millisec)
- 1.5.11 public static boolean holdsLock(Object x)
- 1.5.12 public static Thread currentThread()
- 1.5.13 public static void dumpStack()
- 1.5.14 public void suspend()
- 1.5.15 public void stop()
- 1.5.16 public void resume()
- 1.5.17 public void wait()-current thread to wait until another thread invokes the notify()
- 1.5.18 public void notify()-Wakes up a single thread that is waiting on this object's monitor.
- 1.5.19 public void notifyAll()-Wakes up all the threads that called wait( ) on the same object.
- 2 Multithreading Interview Questions and Answers
- 2.1 What is the difference between processes and threads ?
- 2.2 Explain different ways of creating a thread.Which one would you prefer and why ?
- 2.3 Explain the available thread states in a high-level?
- 2.4 What is the difference between a synchronized method and a synchronized block ?
- 2.5 What’s a deadlock ?
- 2.6 Java Examples - Monitoring a Thread
- 2.7 What is wrong with this code? Heap Vs Stack, Thread safety & Synchronized
Life Cycle of a Thread
Thread States
Following are the stages of the life cycle
NEW
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.
RUNNABLE
After a newly born thread is started, the thread becomes runnable. A thread in this state is considered to be executing its task.
RUNNING
BLOCKED/WAITING
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 executing.
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.
TERMINATED/DEAD
A runnable thread enters the terminated state when it completes its task or otherwise terminates.
Thread Priorities
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.
Runnable Interface
Create a Thread by Implementing a Runnable Interface
If your class is intended to be executed as a thread then you can achieve this by implementing a Runnable interface. You will need to follow three basic steps:
- Step 1:As a first step, you need to implement a run() method provided by a Runnable interface. This method provides an entry point for the thread and you will put your complete business logic inside this method. Following is a simple syntax of the run() method
public void run( )
- Step 2: As a second step, you will instantiate a Thread object using the following constructor
Thread(Runnable threadObj, String threadName);
Where, threadObj is an instance of a class that implements the Runnable interface and threadName is the name given to the new thread.
- Step 3:Once a Thread object is created, you can start it by calling start() method, which executes a call to run( ) method. Following is a simple syntax of start() method
void start();
Example:
class RunnableDemo implements Runnable {
private Thread t;
private String threadName;
RunnableDemo( String name) {
threadName = name;
System.out.println("Creating " + threadName );
}
public void run() {
System.out.println("Running " + threadName );
try {
for(int i = 4; i > 0; i--) {
System.out.println("Thread: " + threadName + ", " + i);
// Let the thread sleep for a while.
Thread.sleep(50);
}
} catch (InterruptedException e) {
System.out.println("Thread " + threadName + " interrupted.");
}
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[]) {
RunnableDemo R1 = new RunnableDemo( "Thread-1");
R1.start();
RunnableDemo R2 = new RunnableDemo( "Thread-2");
R2.start();
}
}
This will produce the following result
Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.
Thread Class
Create a Thread by Extending a Thread Class
The second way to create a thread is to create a new class that extends Thread class using the following two simple steps. This approach provides more flexibility in handling multiple threads created using available methods in Thread class.
- Step 1:You will need to override run( ) method available in Thread class. This method provides an entry point for the thread and you will put your complete business logic inside this method. Following is a simple syntax of run() method
public void run( )
- Step 2:
Once Thread object is created, you can start it by calling start() method, which executes a call to run( ) method. Following is a simple syntax of start() method
void start( );
Example
class ThreadDemo extends Thread {
private Thread t;
private String threadName;
ThreadDemo( String name) {
threadName = name;
System.out.println("Creating " + threadName );
}
public void run() {
System.out.println("Running " + threadName );
try {
for(int i = 4; i > 0; i--) {
System.out.println("Thread: " + threadName + ", " + i);
// Let the thread sleep for a while.
Thread.sleep(50);
}
} catch (InterruptedException e) {
System.out.println("Thread " + threadName + " interrupted.");
}
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[]) {
ThreadDemo T1 = new ThreadDemo( "Thread-1");
T1.start();
ThreadDemo T2 = new ThreadDemo( "Thread-2");
T2.start();
}
}
This will produce the following result
Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.
Thread Methods
public void start()
Starts the thread in a separate path of execution, then invokes the run() method on this Thread object.
public void run()
If this Thread object was instantiated using a separate Runnable target, the run() method is invoked on that Runnable object.
public final void setName(String name)
Changes the name of the Thread object. There is also a getName() method for retrieving the name.
public final void setPriority(int priority)
Sets the priority of this Thread object. The possible values are between 1 and 10.
public final void setDaemon(boolean on)
A parameter of true denotes this Thread as a daemon thread.
public final void join(long millisec) - t1.join(2000);//start second thread after waiting for 2 seconds or if it's dead
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.
- public final void join(): This java thread join method puts the current thread on wait until the thread on which it’s called is dead. If the thread is interrupted, it throws InterruptedException.
- public final synchronized void join(long millis): This java thread join method is used to wait for the thread on which it’s called to be dead or wait for specified milliseconds. Since thread execution depends on OS implementation, it doesn’t guarantee that the current thread will wait only for given time.
- public final synchronized void join(long millis, int nanos): This java thread join method is used to wait for thread to die for given milliseconds plus nanoseconds.
Here is a simple example showing usage of Thread join methods:
package com.journaldev.threads;
public class ThreadJoinExample {
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable(), "t1");
Thread t2 = new Thread(new MyRunnable(), "t2");
Thread t3 = new Thread(new MyRunnable(), "t3");
t1.start();
//start second thread after waiting for 2 seconds or if it's dead
try {
t1.join(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
//start third thread only when first thread is dead
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
t3.start();
//let all threads finish execution before finishing main thread
try {
t1.join();
t2.join();
t3.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("All threads are dead, exiting main thread");
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("Thread started:::"+Thread.currentThread().getName());
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread ended:::"+Thread.currentThread().getName());
}
}
Output of the above program is:
Thread started:::t1
Thread started:::t2
Thread ended:::t1
Thread started:::t3
Thread ended:::t2
Thread ended:::t3
All threads are dead, exiting main thread
public void interrupt()
Interrupts this thread, causing it to continue execution if it was blocked for any reason.
public final boolean isAlive() - It tests if this thread is alive
Returns true if the thread is alive, which is any time after the thread has been started but before it runs to completion.
It tests if this thread is alive. A thread is alive if it has been started and has not yet died. There is a transitional period from when a thread is running to when a thread is not running. After the run() method returns, there is a short period of time before the thread stops. If we want to know if the start method of the thread has been called or if thread has been terminated, we must use isAlive() method. This method is used to find out if a thread has actually been started and has yet not terminated.
// Java program to illustrate
// isAlive()
public class oneThread extends Thread {
public void run()
{
System.out.println("geeks ");
try {
Thread.sleep(300);
}
catch (InterruptedException ie) {
}
System.out.println("forgeeks ");
}
public static void main(String[] args)
{
oneThread c1 = new oneThread();
oneThread c2 = new oneThread();
c1.start();
c2.start();
System.out.println(c1.isAlive());
System.out.println(c2.isAlive());
}
}
Output:
true
true
geeks
geeks
forgeeks
forgeeks
The previous methods are invoked on a particular Thread object. The following methods in the Thread class are static. Invoking one of the static methods performs the operation on the currently running thread.
public static void yield() -> RUNNING to RUNNABLE
Causes the currently running thread to yield to any other threads of the same priority that are waiting to be scheduled.
Suppose there are three threads t1, t2, and t3. Thread t1 gets the processor and starts its execution and thread t2 and t3 are in Ready/Runnable state. Completion time for thread t1 is 5 hour and completion time for t2 is 5 minutes. Since t1 will complete its execution after 5 hours, t2 has to wait for 5 hours to just finish 5 minutes job. In such scenarios where one thread is taking too much time to complete its execution, we need a way to prevent execution of a thread in between if something important is pending. yeild() helps us in doing so.
yield() basically means that the thread is not doing anything particularly important and if any other threads or processes need to be run, they should run. Otherwise, the current thread will continue to run.
Use of yield method:
- Whenever a thread calls java.lang.Thread.yield method, it gives hint to the thread scheduler that it is ready to pause its execution. Thread scheduler is free to ignore this hint.
- If any thread executes yield method , thread scheduler checks if there is any thread with same or high priority than this thread. If processor finds any thread with higher or same priority then it will move the current thread to Ready/Runnable state and give processor to other thread and if not – current thread will keep executing.
Syntax:
// Java program to illustrate yield() method
// in Java
import java.lang.*;
// MyThread extending Thread
class MyThread extends Thread
{
public void run()
{
for (int i=0; i<5 ; i++)
System.out.println(Thread.currentThread().getName()
+ " in control");
}
}
// Driver Class
public class yieldDemo
{
public static void main(String[]args)
{
MyThread t = new MyThread();
t.start();
for (int i=0; i<5; i++)
{
// Control passes to child thread
Thread.yield();
// After execution of child Thread
// main thread takes over
System.out.println(Thread.currentThread().getName()
+ " in control");
}
}
}
Output:
Thread-0 in control
Thread-0 in control
Thread-0 in control
Thread-0 in control
Thread-0 in control
main in control
main in control
main in control
main in control
main in control
Output may vary in machine to machine but chances of execution of yield() thread first is higher than the other thread because main thread is always pausing its execution and giving chance to child thread(with same priority).
Note:
- Once a thread has executed yield method and there are many threads with same priority is waiting for processor, then we can't specify which thread will get execution chance first.
- The thread which executes the yield method will enter in the Runnable state from Running state.
- Once a thread pauses its execution, we can't specify when it will get chance again it depends on thread scheduler.
- Underlying platform must provide support for preemptive scheduling if we are using yield method.
public static void sleep(long millisec)
Causes the currently running thread to block for at least the specified number of milliseconds.
This method causes the currently executing thread to sleep for the specified number of milliseconds, subject to the precision and accuracy of system timers and schedulers.
import java.lang.*;
public class SleepDemo implements Runnable
{
Thread t;
public void run()
{
for (int i = 0; i < 4; i++)
{
System.out.println(Thread.currentThread().getName()
+ " " + i);
try
{
// thread to sleep for 1000 milliseconds
Thread.sleep(1000);
}
catch (Exception e)
{
System.out.println(e);
}
}
}
public static void main(String[] args) throws Exception
{
Thread t = new Thread(new SleepDemo());
// call run() function
t.start();
Thread t2 = new Thread(new SleepDemo());
// call run() function
t2.start();
}
}
Output:
Thread-0 0
Thread-1 0
Thread-0 1
Thread-1 1
Thread-0 2
Thread-1 2
Thread-0 3
Thread-1 3
Note:
- Based on the requirement we can make a thread to be in sleeping state for a specified period of time
- Sleep() causes the thread to definitely stop executing for a given amount of time; if no other thread or process needs to be run, the CPU will be idle (and probably enter a power saving mode).
public static boolean holdsLock(Object x)
Returns true if the current thread holds the lock on the given Object.
public static Thread currentThread()
Returns a reference to the currently running thread, which is the thread that invokes this method.
public static void dumpStack()
Prints the stack trace for the currently running thread, which is useful when debugging a multithreaded application.
public void suspend()
This method puts a thread in the suspended state and can be resumed using resume() method.
public void stop()
This method stops a thread completely.
public void resume()
This method resumes a thread, which was suspended using suspend() method.
public void wait()-current thread to wait until another thread invokes the notify()
Causes the current thread to wait until another thread invokes the notify().
public void notify()-Wakes up a single thread that is waiting on this object's monitor.
Wakes up a single thread that is waiting on this object's monitor.
public void notifyAll()-Wakes up all the threads that called wait( ) on the same object.
Wakes up all the threads that called wait( ) on the same object.
wait, notify, notifyAll have been implemented as final methods in Object, so they are available in all the classes. All three methods can be called only from within a synchronized context.
Example:
This examples shows how two threads can communicate using wait() and notify() method. You can create a complex system using the same concept.
class Chat {
boolean flag = false;
public synchronized void Question(String msg) {
if (flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(msg);
flag = true;
notify();
}
public synchronized void Answer(String msg) {
if (!flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(msg);
flag = false;
notify();
}
}
class T1 implements Runnable {
Chat m;
String[] s1 = { "Hi", "How are you ?", "I am also doing fine!" };
public T1(Chat m1) {
this.m = m1;
new Thread(this, "Question").start();
}
public void run() {
for (int i = 0; i < s1.length; i++) {
m.Question(s1[i]);
}
}
}
class T2 implements Runnable {
Chat m;
String[] s2 = { "Hi", "I am good, what about you?", "Great!" };
public T2(Chat m2) {
this.m = m2;
new Thread(this, "Answer").start();
}
public void run() {
for (int i = 0; i < s2.length; i++) {
m.Answer(s2[i]);
}
}
}
public class TestThread {
public static void main(String[] args) {
Chat m = new Chat();
new T1(m);
new T2(m);
}
}
When the above program is complied and executed, it produces the following result:
Hi
Hi
How are you ?
I am good, what about you?
I am also doing fine!
Great!
Example:
class RunnableDemo implements Runnable {
public Thread t;
private String threadName;
boolean suspended = false;
RunnableDemo( String name) {
threadName = name;
System.out.println("Creating " + threadName );
}
public void run() {
System.out.println("Running " + threadName );
try {
for(int i = 10; i > 0; i--) {
System.out.println("Thread: " + threadName + ", " + i);
// Let the thread sleep for a while.
Thread.sleep(300);
synchronized(this) {
while(suspended) {
wait();
}
}
}
} catch (InterruptedException e) {
System.out.println("Thread " + threadName + " interrupted.");
}
System.out.println("Thread " + threadName + " exiting.");
}
public void start () {
System.out.println("Starting " + threadName );
if (t == null) {
t = new Thread (this, threadName);
t.start ();
}
}
void suspend() {
suspended = true;
}
synchronized void resume() {
suspended = false;
notify();
}
}
public class TestThread {
public static void main(String args[]) {
RunnableDemo R1 = new RunnableDemo( "Thread-1");
R1.start();
RunnableDemo R2 = new RunnableDemo( "Thread-2");
R2.start();
try {
Thread.sleep(1000);
R1.suspend();
System.out.println("Suspending First Thread");
Thread.sleep(1000);
R1.resume();
System.out.println("Resuming First Thread");
R2.suspend();
System.out.println("Suspending thread Two");
Thread.sleep(1000);
R2.resume();
System.out.println("Resuming thread Two");
} catch (InterruptedException e) {
System.out.println("Main thread Interrupted");
}try {
System.out.println("Waiting for threads to finish.");
R1.t.join();
R2.t.join();
} catch (InterruptedException e) {
System.out.println("Main thread Interrupted");
}
System.out.println("Main thread exiting.");
}
}
The above program produces the following output:
Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 10
Running Thread-2
Thread: Thread-2, 10
Thread: Thread-1, 9
Thread: Thread-2, 9
Thread: Thread-1, 8
Thread: Thread-2, 8
Thread: Thread-1, 7
Thread: Thread-2, 7
Suspending First Thread
Thread: Thread-2, 6
Thread: Thread-2, 5
Thread: Thread-2, 4
Resuming First Thread
Suspending thread Two
Thread: Thread-1, 6
Thread: Thread-1, 5
Thread: Thread-1, 4
Thread: Thread-1, 3
Resuming thread Two
Thread: Thread-2, 3
Waiting for threads to finish.
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.
Main thread exiting.
Multithreading Interview Questions and Answers
What is the difference between processes and threads ?
- A process is an execution of a program, while a Thread is a single execution sequence within a process. A process can contain multiple threads. A Thread is sometimes called a lightweight process.
Explain different ways of creating a thread.Which one would you prefer and why ?
- There are three ways that can be used in order for a Thread to be created:
- A class may extend the Thread class.
- A class may implement the Runnable interface.
- An application can use the Executor framework, in order to create a thread pool.
- The Runnable interface is preferred, as it does not require an object to inherit the Thread class. In case your application design requires multiple inheritance, only interfaces can help you. Also, the thread pool is very efficient and can be implemented and used very easily.
- There are three ways that can be used in order for a Thread to be created:
Explain the available thread states in a high-level?
- During its execution, a thread can reside in one of the following states:
- Runnable: A thread becomes ready to run, but does not necessarily start running immediately.
- Running: The processor is actively executing the thread code.
- Waiting: A thread is in a blocked state waiting for some external processing to finish.
- Sleeping: The thread is forced to sleep.
- Blocked on I/O: Waiting for an I/O operation to complete.
- Blocked on Synchronization: Waiting to acquire a lock.
- Dead: The thread has finished its execution.
- During its execution, a thread can reside in one of the following states:
What is the difference between a synchronized method and a synchronized block ?
- In Java programming, each object has a lock. A thread can acquire the lock for an object by using the synchronized keyword. The synchronized keyword can be applied in a method level (coarse grained lock) or block level of code (fine grained lock).
- How does thread synchronization occurs inside a monitor ? What levels of synchronization can you apply ?
- The JVM uses locks in conjunction with monitors. A monitor is basically a guardian that watches over a sequence of synchronized code and ensuring that only one thread at a time executes a synchronized piece of code. Each monitor is associated with an object reference. The thread is not allowed to execute the code until it obtains the lock.
What’s a deadlock ?
- A condition that occurs when two processes are waiting for each other to complete, before proceeding. The result is that both processes wait endlessly.
- How do you ensure that N threads can access N resources without deadlock ?
- A very simple way to avoid deadlock while using N threads is to impose an ordering on the locks and force each thread to follow that ordering. Thus, if all threads lock and unlock the mutexes in the same order, no deadlocks can arise.
Java Examples - Monitoring a Thread
How to monitor a thread's status?
Following example demonstrates how to monitor a thread's status by extending Thread class and using currentThread.getName() method.
class MyThread extends Thread {
boolean waiting = true;
boolean ready = false;
MyThread() {
}
public void run() {
String thrdName = Thread.currentThread().getName();
System.out.println(thrdName + " starting.");
while(waiting) System.out.println("waiting:"+waiting);
System.out.println("waiting...");
startWait();
try {
Thread.sleep(1000);
} catch(Exception exc) {
System.out.println(thrdName + " interrupted.");
}
System.out.println(thrdName + " terminating.");
}
synchronized void startWait() {
try {
while(!ready) wait();
} catch(InterruptedException exc) {
System.out.println("wait() interrupted");
}
}
synchronized void notice() {
ready = true;
notify();
}
}
public class new_class {
public static void main(String args[]) throws Exception {
MyThread thrd = new MyThread();
thrd.setName("MyThread #1");
showThreadStatus(thrd);
thrd.start();
Thread.sleep(50);
showThreadStatus(thrd);
thrd.waiting = false;
Thread.sleep(50);
showThreadStatus(thrd);
thrd.notice();
Thread.sleep(50);
showThreadStatus(thrd);
while(thrd.isAlive())
System.out.println("alive");
showThreadStatus(thrd);
}
static void showThreadStatus(Thread thrd) {
System.out.println(thrd.getName()+" Alive:="+thrd.isAlive()+" State:=" + thrd.getState() );
}
}
Result
The above code sample will produce the following result.
main Alive=true State:=running
What is wrong with this code? Heap Vs Stack, Thread safety & Synchronized
A very simple code that should print numbers from 7 to 21. But does it?
import java.util.concurrent.TimeUnit;
class Counter extends Thread {
//instance variable
Integer count = 0;
// method where the thread execution will start
public void run() {
int fixed = 6; //local variable
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + ": result="
+ performCount(fixed));
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// let’s see how to start the threads
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName() + " is executing..." );
Counter counter = new Counter();
//5 threads
for (int i = 0; i < 5; i++) {
Thread t = new Thread(counter);
t.start();
}
}
//multiple threads can access me concurrently
private int performCount(int fixed) {
return (fixed + ++count);
}
}
Above code is NOT Thread-safe If you run it multiple times, you will see that some numbers get repeated as shown below. You get five”15″s and three “12”s. The result will be unpredictable and you will get different results each time you run it.
main is executing...
Thread-1: result=7
Thread-2: result=8
Thread-3: result=9
Thread-4: result=10
Thread-5: result=11
Thread-2: result=12
Thread-3: result=12
Thread-4: result=12
Thread-5: result=13
Thread-1: result=14
Thread-2: result=15
Thread-3: result=15
Thread-4: result=15
Thread-5: result=15
Thread-1: result=15
What is happening under the covers in terms of Heap Vs Stack memory & thread-safety
As shown below in the diagaram, the local variable “fixed”, and the reference “counter” to the instance of the class “Counter” are stored in the stack. The instance of “Counter”, i,e. the object itself is stored in the heap. So, it will be shared by all the threads. The “++count” operation is not atomic and performs 3 operations under the covers:
- Step 1: get value of count from heap
- Step 2: add 1 to count (i.e. count = count + 1)
- Step 3: write the new value back to the heap memory
So, it is possible that 5 threads read the same value of say “count = 8” and increment them all to “9”, and then when added with the fixed value of 6, resulting in five “15”s. Each time you run, you get different results. The above code is unpredictable.
How to fix the concurrency issue?
The above thread safety issue can be fixed two ways by controlling the access to the shared object “counter”.
Solution 1: Synchronized i.e. a lock on the performCount() method
This will put a lock on “counter” object so that when one thread is performing the other threads have to wait for the lock.
import java.util.concurrent.TimeUnit;
class Counter extends Thread {
//instance variable
Integer count = 0;
// method where the thread execution will start
public void run() {
int fixed = 6;
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + ": result="
+ performCount(fixed));
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// let’s see how to start the threads
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName() + " is executing..." );
Counter counter = new Counter();
//5 threads
for (int i = 0; i < 5; i++) {
Thread t = new Thread(counter);
t.start();
}
}
private synchronized int performCount(int fixed) {
return (fixed + ++count);
}
}
Output:
main is executing...
Thread-1: result=7
Thread-3: result=8
Thread-2: result=9
Thread-4: result=10
Thread-5: result=11
Thread-4: result=13
Thread-1: result=12
Thread-2: result=14
Thread-3: result=15
Thread-5: result=16
Thread-4: result=18
Thread-3: result=19
Thread-1: result=20
Thread-5: result=21
Thread-2: result=17
Why is locking of a method for thread safety is called “synchronized” and not “locked”?
When a method or block of code is locked with the reserved “synchronized” key word in Java, the memory (i.e. heap) where the shared data is kept is synchronized. This means,
When a synchronized block or method is entered after the lock has been acquired by a thread, it first reads (i.e. synchronizes) any changes to the locked object from the main heap memory to ensure that the thread that has the lock has the current info before start executing.
After the synchronized block has completed and the thread is ready to relinquish the lock, all the changes that were made to the object that was locked is written or flushed back (i.e. synchronized) to the main heap memory so that the other threads that acquire the lock next has the current info.
Solution 2: AtomicInteger so that the increment operation is atomic
The “incrementAndGet()” on AtomicInteger happens atomically so that two or more threads cannot read the same value and increment them to the same result.
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
class Counter extends Thread {
//instance variable
AtomicInteger count = new AtomicInteger();
// method where the thread execution will start
public void run() {
int fixed = 6;
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + ": result="
+ performCount(fixed));
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// let’s see how to start the threads
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName() + " is executing..." );
Counter counter = new Counter();
//5 threads
for (int i = 0; i < 5; i++) {
Thread t = new Thread(counter);
t.start();
}
}
private int performCount(int fixed) {
return (fixed + count.incrementAndGet());
}
}
Output:
main is executing...
Thread-1: result=7
Thread-2: result=8
Thread-3: result=9
Thread-4: result=10
Thread-5: result=11
Thread-1: result=16
Thread-3: result=13
Thread-2: result=12
Thread-4: result=15
Thread-5: result=14
Thread-1: result=20
Thread-5: result=19
Thread-2: result=21
Thread-4: result=17
Thread-3: result=18