Threads are independent execution units in a process. They increase effectiveness and responsiveness by enabling the concurrent operation of multiple tasks within a single process.
In multithreading, when a thread is inactive, it is either in a blocked state or in a waiting state.
A thread moves to the blocked state when it wants to access an object that another thread has locked. Once that resource is available (unlocked) for the thread, it is no longer blocked and moves to the runnable state. For example, if a thread tries to access a shared resource that is currently being used by another thread, it will be blocked until that resource becomes available.
A thread is in a waiting state when it is waiting for a specific event or a signal from another object. The thread will remain in this state until another object send the signal using notify()
or notifyAll()
.
Once this signal is received, it becomes runnable.
Note: The thread in a waiting state might not be runnable after being notified using
notify()
ornotifyAll()
. If two or more threads have been notified, they will all be moved to the blocked state to obtain the lock. In this case, the highest priority thread will get the lock and run first.
Let's assume we have three threads named Process1, Process2, and Process3. These three threads are competing with one another for access to a printer that is our shared resource. Each thread must obtain a lock on the printer before printing anything, but only one thread can hold the lock at a time.
The following steps describes how the wait and blocked states might be involved:
Step 1: Process1 is successful in gaining access to the printer's lock. It starts printing its document and keeps the lock while printing.
Step 2: When Process2 tries to get the printer's lock, it finds that Process1 already has it. While in the blocked state, Process2 waits for the lock to become available. Process2 cannot progress and is not using any system resources while it is blocked.
Step 3: Process3 also tries to get the printer's lock but discovers that Process 1 still possesses it. Process3 calls the lock object's wait()
method instead of going into the blocked state. Process3 is now waiting, which means it will voluntarily stop running while waiting for another thread to call notify()
or notifyAll()
on the lock object. Until Process3 receives a signal from another thread, it is not using any system resources.
Step 4: Once Process1 has finished printing, the printer's lock is released. The scheduler will unblock Process2, allowing it to acquire the lock and start printing as soon as it becomes available. Process3 waits until it receives a signal from another thread, at which point it will be runnable and resume execution.
Note: When
notifyAll()
was called, both Process2 and Process3 were woken, but Process2 was able to get the lock before Process3 had a chance. Process2 may have had priority over Process3 because it had been waiting longer, or the JVM may have used another scheduling technique.
When multiple threads try for a lock, it is difficult to predict precisely which thread will obtain it first. Therefore, it's important to properly synchronize access to shared resources in a multi-threaded program.
This is how the wait and blocked states are used to control thread execution while making sure that shared resources are used in a thread-safe way.
The scheduler mandates the blocked state, whereas the waiting state is self-imposed by the thread to maintain
A thread is inactive when in the blocked or waiting state. When in these states, the thread does not consume any CPU cycles.