Java Threads
Contents
- 1 Life Cycle of a Thread
- 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.
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