Threads in Java
Inter-thread Communication in Java
Inter-thread communication or Co-operation is all about allowing synchronized threads to communicate with each other.
Inter-thread communication is a mechanism in which a thread is paused running in its critical section and another thread is allowed to enter (or lock) in the same critical section to be executed. It is implemented by following methods of Object class:
wait()
notify()
notifyAll()
Monitor Lock
- In java monitor lock is the mechanism used to enforce mutual exclusion and synchronization between threads when accessing shared resources. It ensures only one thread at a time can execute a block of code or method that is synchronized on same monitor lock
Key Points about Monitor lock
In Java every object has its own associated monitor lock. This lock is used by threads to coordinate access to critical section of code.
A thread must acquire monitor lock of an object before entering a synchronized block or method.
A monitor lock is automatically released when :
The thread exists the synchronized block or method.
The thread calls
wait()
on the object (this releases the lock temporarily).It is not released when thread is interrupted or goes in sleep() state.
Behavior in Multithreaded Context
If one thread holds the monitor lock:
Other threads trying to access the synchronized block or method will be blocked.
These threads remain in Blocked State until the lock is released.
If thread calls
wait()
It releases the monitor lock temporarily and enters the waiting state.
It will need to re-acquire the monitor lock before resuming execution after being notified.
Synchronization in java
Synchronization in Java is the process that allows only one thread at a particular time to complete a given task entirely. By default, the JVM gives control to all the threads present in the system to access the shared resource, due to which the system approaches race condition
- Synchronized method in java
class Demo {
public synchronized void access() {
System.out.println("Hello from thread")
}
}
Methods for Inter-thread communication
1) wait() method
The
wait()
method causes current thread to release the lock and wait until either another thread invokes thenotify()
method or thenotifyAll()
method for this same object, or a specified amount of time has elapsed.The current thread must own this object's monitor, so it must be called from the synchronized method only otherwise it will throw exception
2) notify() method
- The
notify()
method wakes up a single thread that is waiting on this object's monitor. If any threads are waiting on this object, one of them is chosen to be awakened. The choice is arbitrary and occurs at the discretion of the implementation.
3) notifyAll() method
- Wakes up all threads that are waiting on this object's monitor.
Difference between wait and sleep?
wait() | sleep() |
The wait() method releases the lock. | The sleep() method doesn't release the lock. |
It is a method of Object class | It is a method of Thread class |
It is the non-static method | It is the static method |
It should be notified by notify() or notifyAll() methods | After the specified amount of time, sleep is completed. |
Example 1 for Synchronization
- SynchronizationDemo.java
package com.codefreak.thread;
public class SynchronizedDemo {
public synchronized void task1() {
try {
System.out.println("Task 1 executing");
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
}
public synchronized void task2() {
try {
System.out.println("Task 2 executing");
} catch (Exception e) {
e.printStackTrace();
}
}
public void task3() {
System.out.println("Task 3 executing");
}
}
- MainClass.java
public class MainClass {
public static void main(String[] args) {
SynchronizedDemo synchronizedDemo = new SynchronizedDemo();
Thread t1 = new Thread(() -> {
synchronizedDemo.task1();
});
Thread t2 = new Thread(() -> {
synchronizedDemo.task2();
});
Thread t3 = new Thread(() -> {
synchronizedDemo.task3();
});
t1.start();
t2.start();
t3.start();
}
}
Explanation
Here when thread starts execution any thread is invoked first based on jvm, as of now let’s consider t1→t2→t3 in this way they have been invoked by jvm.
First thread1 will execute its method and will go in
timed waiting
state for 5 second and it will execute wholetask1
method.Then thread3 will execute its method as it is not
synchronized
the lock will not be acquired.Then thread2 will execute its method this is
synchronized
method so lock will be acquired by thread2.
Example 2 for Synchronization
- SharedResource.java
package com.codefreak.thread;
public class SharedResource {
private List<Integer> items;
private boolean itemAvailable;
public SharedResource() {
this.items = new ArrayList<Integer>();
this.itemAvailable = false;
}
public synchronized void addItem(int item) {
try {
items.add(item);
itemAvailable = true;
notifyAll();
System.out.println("Procuder thread added item " + item);
} catch (Exception e) {
e.printStackTrace();
}
}
public synchronized void getItem() {
try {
while (!itemAvailable) {
System.out.println("Consumer thread going in wating state");
wait(); // releases the lock
}
System.out.println("Consumer thread Printing item");
for (int item : items) {
System.out.print(item + " -> ");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
- MainClass.java
public class MainClass {
public static void main(String[] args) {
SharedResource sharedResource = new SharedResource();
Thread producerThread = new Thread(() -> {
try {
Thread.sleep(3000);
} catch (Exception e) {}
sharedResource.addItem(10);
});
Thread consumerThread = new Thread(() -> {
sharedResource.getItem();
});
producerThread.start();
consumerThread.start();
}
}
Important Point to Note:
A thread does not immediately acquire a lock when it starts. It will only acquire a lock when it enters a synchronized method or block, and it will only acquire the lock if no other thread is holding it.
If the lock is already help by other thread then the thread will be blocked until it acquires a lock.
Example:
- SynchronizedDemo.java
package com.codefreak.thread;
public class SynchronizedDemo {
public synchronized void task1() {
System.out.println("Inside task 1");
try {
System.out.println("--Task 1 executing---");
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Exiting Task 1");
}
public synchronized void task2() {
System.out.println("Inside task 2");
try {
System.out.println("---Task 2 executing---");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Exiting Task 2");
}
}
Inside task 1
--Task 1 executing---
Exiting Task 1
Inside task 2
---Task 2 executing---
Exiting Task 2
Inside task 2
---Task 2 executing---
Exiting Task 2
Inside task 2
---Task 2 executing---
Exiting Task 2
Inside task 2
---Task 2 executing---
Exiting Task 2
Inside task 2
---Task 2 executing---
Exiting Task 2
Inside task 1
--Task 1 executing---
Exiting Task 1
Inside task 1
--Task 1 executing---
Exiting Task 1
Inside task 1
--Task 1 executing---
Exiting Task 1
Inside task 1
--Task 1 executing---
Exiting Task 1