In this tutorial series, we are learning multithreading in Java. This is the second article in the series.
In the first article, we learned about the basics of multithreading.
Prevent Thread Execution
We can prevent the thread execution by using the following methods:
yield()
join()
sleep()
yield()
yield() method pause the current execution of the thread and give the chance for waiting thread of the same priority. If there are no waiting thread having the same priority or all other waiting thread having low priority then the same thread continues its execution. If multiple threads are waiting with the same priority then which waiting thread will get the chance we cant to expect, it depends on the thread schedular. A thread which yield, when it will get a chance once again it depends on thread schedular and we cant expect exactly.
package mt; class Mythread extends Thread { @Override public void run() { for (int i = 0; i < 3; i++) { System.out.println("child thread!!!"); Thread.yield(); } } } public class ThreadYieldDemo { public static void main(String[] args) { Mythread mythread = new Mythread(); mythread.start(); mythread.setPriority(5); for (int i = 0; i < 3; i++) { System.out.println("main thread!!!"); } } } /* output - main thread!!! main thread!!! main thread!!! child thread!!! child thread!!! child thread!!! */
In the above program, the main thread will complete execution before the child thread. If we commenting Thread.yield() line, then both thread run simultaneously and we cant except which thread will complete first.
join()
If a thread wants to wait until completing some other thread then we should go for the join() method. For example thread t1 wants to wait until completing t2, then t1 has to call t2.join(). If t1 executes t2.join(), then immediately t1 will be entered into the waiting state until t2 completes. Once t2 terminate then t1 can continue its execution.
Methods: -
public final void join() throws InterruptedException //default milliseconds = 0
public final synchronized void join(long millis) throws InterruptedException
public final synchronized void join(long millis, int nanos) throws InterruptedException
join() throws InterruptedException which is checked exception, hence we should compulsorily handle this exception either by using a try-catch block or throws, otherwise we will get a compile-time error.
Case 1: main thread waits for completion of the child thread:
package mt; class MyThread extends Thread { public void run() { for (int i = 0; i < 3; i++) { System.out.println("Child method."); } } } public class ThreadJoinDemo { public static void main(String[] args) throws InterruptedException { System.out.println("Main thread will wait until child completes..."); MyThread mt = new MyThread(); mt.start(); mt.join(); // Main thread for completion of child thread for (int i = 0; i < 3; i++) { System.out.println("Main method"); } } } /* output - Main thread will wait until child completes... Child method. Child method. Child method. Main method Main method Main method */
Case 2: child thread waits for completion of the main thread :
package mt; class MyTh extends Thread { static Thread mainThread; @Override public void run() { try { mainThread.join(); // child thread waits for completion of main thread } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 3; i++) { System.out.println("Child Thread"); } } } public class ThreadJoinDemo_ChildWaitForMain { public static void main(String[] args) { MyTh.mainThread = Thread.currentThread(); MyTh myTh = new MyTh(); myTh.start(); for (int i = 0; i < 3; i++) { System.out.println("Main Thread"); } } } /* Output - Main Thread Main Thread Main Thread Child Thread Child Thread Child Thread */
Case 3: child thread waits for the main thread and the main thread waits for the child thread (deadlock):
package mt; class JoinChildThread extends Thread { static Thread mainThread; @Override public void run() { try { mainThread.join(); // Child thread waiting for main thread completion } catch (InterruptedException e) { System.out.println(e); } for (int i = 0; i < 3; i++) { System.out.println("Child thread : " + i); } } } public class ThreadJoinDemo_DeadLock { public static void main(String[] args) throws InterruptedException { JoinChildThread thread = new JoinChildThread(); JoinChildThread.mainThread = Thread.currentThread(); thread.start(); thread.join(); // Main thread waiting for child thread completion for (int i = 0; i < 3; i++) { System.out.println("Main thread : " + i); } } } /* Because of deadLock no output Both thread waiting for each other */
Case 4: main thread waits for completion of itself and leading deadlock :
package mt; public class ThreadJoinDemo_SelfWaiting_Dedlock { public static void main(String[] args) throws InterruptedException { Thread.currentThread().join(); // Main thread waiting for completion of main thread - Deadlock for (int i = 0; i < 3; i++) { System.out.println("Main "+i); } } }
sleep()
Thread.sleep causes the current thread to suspend execution for a specified period. Using Thread.sleep we can make processor time available to the other threads.
Methods: -
public static native void sleep(long millis) throws InterruptedException
public static void sleep(long millis, int nanos) throws InterruptedException
A thread can interrupt a sleeping thread or waiting thread by using the interrupt() method of thread class.
package mt; public class InterrutedExceptionDemo { public static void main(String[] args) { Thread t = new Thread(new Runnable() { @Override public void run() { try { System.out.println("Started thread"); for (int i = 0; i < 10; i++) { System.out.println(i); Thread.sleep(5000); } } catch (InterruptedException e) { System.out.println("exiting eblocking thread "); } } }); t.start(); t.interrupt(); // Child thread is interrupted. } } /* Started thread 0 exiting eblocking thread */
Whenever we are calling the interrupt() method, if the target thread is not in waiting/sleeping state then there is no impact of an interrupt() call. Interrupt call waits until the target thread enters into a sleeping/waiting state. If the target thread enters into a waiting/sleeping state then immediately interrupt() call interrupt the thread.
InterThread Communication
Two threads can communicate with each other by using wait(), notify(), notifyAll() methods.
The thread which is expecting an update operation is responsible for calling the wait() method then immediately that thread enters into the waiting state. The thread which performs the update operation is responsible to call the notify() method. Then the waiting thread gets that notification and continues its execution with the updated value.
Note - wait(), notify(), notifyAll() methods present in the Object class but not in the thread class because the thread can call this method on any java object.
To call wait(), notify(), notifyAll() methods on any java object, the thread should be the owner of that object i.e. thread should have the lock of that object i.e. thread should be inside a synchronized area. Hence we can call wait(), notify(), notifyAll() methods only from synchronized area otherwise we will get RuntimeException - IllegalMonitorStateException
Methods
public final void wait() throws InterruptedException;
public final native void wait(long timeout) throws InterruptedException;
public final void wait(long timeout, int Nanos) throws InterruptedException;
public final native void notify();
public final native void notifyAll();
Every wait() method throws InterruptedException which is a checked exception. Hence whenever we are using the wait() method, compulsory we should handle this InterruptedException by using the try-catch or by throws keyword, otherwise, we will get a compile-time error.
Producer-Consumer problem
package CoreJava.MultiThreading;
import java.util.LinkedList;
import java.util.List;
class Producer implements Runnable{
private List<Integer> sharedQueue;
private int MAX_SIZE = 2;
Producer(List<Integer> sharedQueue){
this.sharedQueue = sharedQueue;
}
@Override
public void run() {
for (int i = 1; i < 10; i++) {
try {
produce(i);
}catch (InterruptedException e){}
}
}
private void produce(int i) throws InterruptedException {
synchronized (sharedQueue){
if (sharedQueue.size()==MAX_SIZE){
System.out.println("Queue is full. Producer is waiting for Consumer to consume");
sharedQueue.wait();
}else {
sharedQueue.add(i);
Thread.sleep(1000);
System.out.println("Produced : "+i);
sharedQueue.notify();
}
}
}
}
class Cosumer implements Runnable{
private List<Integer> sharedQueue;
private int MIN_SIZE = 0;
Cosumer(List<Integer> sharedQueue){
this.sharedQueue = sharedQueue;
}
@Override
public void run() {
for (int i = 1; i < 10; i++) {
try{
consume();
}catch (InterruptedException e){
}
}
}
private void consume() throws InterruptedException {
synchronized (sharedQueue){
if (sharedQueue.size()==MIN_SIZE){
System.out.println("Queue is empty. Waiting for producer to produce");
sharedQueue.wait();
}else {
int num = sharedQueue.remove(0);
System.out.println("Consumed : "+num);
sharedQueue.notify();
}
}
}
}
public class ProducerConsumerDemo {
public static void main(String[] args) {
List<Integer> sharedQueue = new LinkedList<>();
Producer p = new Producer(sharedQueue);
Cosumer c = new Cosumer(sharedQueue);
Thread t1 = new Thread(p);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
Output
Produced : 1
Produced : 2
Queue is full. Producer is waiting for Consumer to consume
Consumed : 1
Consumed : 2
Queue is empty. Waiting for producer to produce
Produced : 4
Produced : 5
Queue is full. Producer is waiting for Consumer to consume
Consumed : 4
Consumed : 5
Queue is empty. Waiting for producer to produce
Produced : 7
Produced : 8
Queue is full. Producer is waiting for Consumer to consume
Consumed : 7
Consumed : 8
Queue is empty. Waiting for producer to produce
Difference between notify() and notifyAll()
-notify() is used to notify waiting thread.
-notifyAll() is used to notify all waiting threads.
If multiple threads are waiting then only one thread will be notified depends on JVM and the remaining threads continue in the waiting thread.
Thread LifeCycle
The thread life cycle is controlled by JVM.
A thread lies only in one of the following states at any instant.
-New
-Runnable/Ready
-Running
-Blocked
-Waiting
-Sleeping
-Terminated
In this tutorial, we learned about how to prevent thread execution using the yield(), join(), sleep() method, Interthread communication using wait(), notify(), notifyAll() methods, producer-consumer problem, and life-cycle of a thread in Java.