Multi Threading & Executor Service: 12.1) What Is A Thread?
Multi Threading & Executor Service: 12.1) What Is A Thread?
// 1* 2* 3* 4* 5* 6* 7* 8* 9* 10* 11* 12* 13* 14* 15* 16* 17* 18* 19* 20* 21* 22* 23* 24* 25*
26* 27* 28* 29* 30* 31* 32* 33* 34* 35* 36* 37* 38* 39* 40* 41* 42* 43* 44* 45* 46* 47* 48*
49* 50* 51* 52* 53* 54* 55* 56* 57* 58* 59* 60* 61* 62* 63* 64* 65* 66* 67* 68* 69* 70* 71*
72* 73* 74* 75* 76* 77* 78* 79* 80* 81* 82* 83* 84* 85* 86* 87* 88* 89* 90* 91* 92* 93* 94*
95* 96* 97* 98* 99* 100*
// Completed * task
//
1$ 2$ 3$ 4$ 5$ 6$ 7$ 8$ 9$ 10$ 11$ 12$ 13$ 14$ 15$ 16$ 17$ 18$ 19$ 20$ 21$ 22$ 23$ 24$ 25$ 26$
27$ 28$ 29$ 30$ 31$ 32$ 33$ 34$ 35$ 36$ 37$ 38$ 39$ 40$ 41$ 42$ 43$ 44$ 45$ 46$ 47$ 48$ 49$ 50
$ 51$ 52$ 53$ 54$ 55$ 56$ 57$ 58$ 59$ 60$ 61$ 62$ 63$ 64$ 65$ 66$ 67$ 68$ 69$ 70$ 71$ 72$ 73$
74$ 75$ 76$ 77$ 78$ 79$ 80$ 81$ 82$ 83$ 84$ 85$ 86$ 87$ 88$ 89$ 90$ 91$ 92$ 93$ 94$ 95$ 96$ 97
$ 98$ 99$ 100$
// Completed $ task
// 1# 2# 3# 4# 5# 6# 7# 8# 9# 10# 11# 12# 13# 14# 15# 16# 17# 18# 19# 20# 21# 22# 23# 24# 25#
26# 27# 28# 29# 30# 31# 32# 33# 34# 35# 36# 37# 38# 39# 40# 41# 42# 43# 44# 45# 46# 47# 48#
49# 50# 51# 52# 53# 54# 55# 56# 57# 58# 59# 60# 61# 62# 63# 64# 65# 66# 67# 68# 69# 70# 71#
72# 73# 74# 75# 76# 77# 78# 79# 80# 81# 82# 83# 84# 85# 86# 87# 88# 89# 90# 91# 92# 93# 94#
95# 96# 97# 98# 99# 100#
// Completed # task
// Total Time taken(in ms) is: 57
Task(char symbol) {
this.symbol = symbol;
}
public char getSymbol() {
return symbol;
}
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.printf("%d%c ", i, symbol);
}
System.out.println("\n" + Thread.currentThread().getName() + " : Completed " + symbol
+ " task");
}
}
class Main {
public static void main(String[] args) {
Task t1 = new Task('*');
Task t2 = new Task('$');
Task t3 = new Task('#');
long start_time = System.currentTimeMillis();
t1.start();
t2.start();
t3.start();
long end_time = System.currentTimeMillis();
System.out.println("Total Time taken(in ms) is: " + (end_time - start_time));
System.out.printf("%s : completed", Thread.currentThread().getName());
}
}
// 1# 2# 3# 4# 5# 6# 7# 8# 9# 10# 11# 12# 13# 14# 15# 16# 17# 18# 19# 20# 21#
// 22# 23# 24# 25# 26# 27# 28# 29# 30# 31# 32# 33# 34# 35# 36# 37# 38# 39# 40#
// 41# 42# 43# 44# 45# 46# 47# 1$ 2$ 3$ 4$ 5$ 6$ 7$ 8$ 9$ 10$ 11$ 12$ 13$ 14$
// 15$ 16$ 17$ 18$ 19$ 20$ 21$ 22$ 23$ 24$ 25$ 26$ 27$ 28$ 29$ 30$ 31$ 32$ 33$
// 34$ 35$ 36$ 37$ 38$ 39$ 40$ 41$ 42$ 43$ 44$ 45$ 46$ 47$ 48$ 49$ 50$ 51$ 52$
// 53$ 54$ 55$ 56$ 57$ 58$ 59$ 60$ 61$ 62$ 63$ 64$ 65$ 66$ 67$ 68$ 69$ 70$ 71$
// 72$ 73$ 74$ 75$ 76$ 77$ 78$ 79$ 80$ 81$ 82$ 83$ 84$ 85$ 86$ 87$ 88$ 89$ 90$
// 91$ 92$ 93$ 94$ 95$ 96$ 97$ 98$ 99$ 100$ 1* 2* 3* 4* 5* 6* 7* 8* 9* 10* 11*
// 12* 13* 14* 15* 16* 17* 18* 19* 20* 21* 22* 23* 24* 25* 26* 27* 28* 29* 30*
// 31* 32* 33* 34* 35* 36* 37* 38* 39* 40* 41* 42* 43* 44* 45* 46* 47* 48* 49*
// 50* 51* 52* 53* 54* 55* 56* 57* 58* 59* 60* 61* 62* 63* 64* 65* 66* 67* 68*
// 69* 70* 71* 72* 73* 74* 75* 76* 77* 78* 79* 80* 81* 82* 83* 84* Total Time
// taken(in ms) is: 0
// 48# 49# 50# 51# 52# 53# 54# 55# 56# 57# 58# 59# 60# 61# 62# 63# 64# 65# 66#
// 67# 68# 69# 70#
// Thread-1 : Completed $ task
// 85* 86* 87* 88* 89* 90* 91* 92* 93* 94* 95* 96* 97* 98* 99* 100* main :
// completed71#
// Thread-0 : Completed * task
// 72# 73# 74# 75# 76# 77# 78# 79# 80# 81# 82# 83# 84# 85# 86# 87# 88# 89# 90#
// 91# 92# 93# 94# 95# 96# 97# 98# 99# 100#
// Thread-2 : Completed # task
A thread is created but not yet started. It has been initialized but start()
New
has not been called.
The thread is ready for execution. The start() method has been called, and
Runnable
it is waiting to be scheduled by the JVM.
The thread is actively executing its tasks. The JVM is actively scheduling
Running
the thread to run.
The thread is alive but not executing. It may be waiting for resources,
Blocked/Sleeping/Waiting
locks, or other conditions to be met.
The thread has finished execution or was stopped. It no longer runs and
Terminated
its lifecycle has ended.
The synchronized keyword in Java ensures that only one thread can execute a block of code at a
time, providing mutual exclusion and preventing race conditions.
When a thread enters a synchronized block or method, it acquires a lock on the object (for
instance methods) or on the class (for static methods).
Changes made by threads inside a synchronized block are visible to all other threads.
1. Instance method → locks the object
2. Static method → locks the class
public class Counter {
private int count = 0;
SynchronizedThread(Counter count) {
this.count = count;
}
@Override
public void run() {
for (int i = 1; i <= 100000; i++) {
count.increment();
}
}
}
1. sleep(long millis):
sleep should not be called on a specific thread object because it is a static method of the Thread
class. This means that Thread.sleep(milliseconds) pauses the currently executing thread,
regardless of which thread object it is called on. If we write Thread.sleep(timeInMilliseconds);
inside the main method, the main thread will transition from the running state to the timed
waiting state, and then back to the running state after the specified duration and sleep() need to
handle the InterruptedException.
2. yield():
Causes the currently executing threads to pause and allow other threads to execute. It's a way of
suggesting that other threads of the same priority can run.
3. wait():
Causes the current thread to wait until another thread invokes the notify() or notifyAll() method
for this object. It releases the lock held by this thread.
4. notify():
Wakes up a single thread that is waiting on the objects monitor. If any threads are waiting, one is
chosen to be awakened.
5. notifyAll():
Wakes up all the threads that are waiting on the object’s monitor.
CHALLENGES
1) Write a program that creates 2 threads. Each thread should print “Hello form Thread X”, where X
is the no.of the thread(1 or 2), 10 times then terminate.
public class HelloThread extends Thread {
private final int threadNo;
If we call t1.run() instead of t1.start(), the run() method will be executed just like a normal method
call, and it will not start a new thread. As a result, the code inside run() will execute in the current
thread, which is typically the main thread. So Thread.currentThread().getName() will return main.
// (1)Hello form Thread-2
// (2)Hello form Thread-2
// (3)Hello form Thread-2
// (4)Hello form Thread-2
// (5)Hello form Thread-2
// (6)Hello form Thread-2
// (1)Hello form Thread-1
// (2)Hello form Thread-1
// (3)Hello form Thread-1
// (4)Hello form Thread-1
// (5)Hello form Thread-1
// (6)Hello form Thread-1
// (7)Hello form Thread-1
// (8)Hello form Thread-1
// (9)Hello form Thread-1
// (10)Hello form Thread-1
// (7)Hello form Thread-2
// (8)Hello form Thread-2
// (9)Hello form Thread-2
// (10)Hello form Thread-2
2) Write a program that starts a thread and prints its state after each significant event (creation,
starting and termination). use Thread.sleep() to simulate long-running tasks and
Thread.getState() to print the thread’s state.
public class PrintOdd extends Thread {
@Override
public void run() {
for (int i = 1; i <= 100; i += 2) {
System.out.print(i + " ");
}
}
}
// NEW
// RUNNABLE
// 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49 51 53
// 55 57 59 61 63 65 67 69 71 73 75 77 79 81 83 85 87 89 91 93 95 97 99
// TERMINATED
Note:
The run() method (used in threads) is not allowed to say it throws a checked exception like
InterruptedException. This is because the original run() method in the Runnable interface doesn't
throw any checked exceptions.
So, if you use Thread.sleep() inside the run() method — which can throw InterruptedException — you
must catch that exception using a try-catch block.
public void run() throws InterruptedException { // ❌ Not allowed
Thread.sleep(1000);
}
So basically:
run() can’t use throws InterruptedException.
You have to handle the exception inside the method using try-catch.
If you want to stop or report the error, you can wrap it in a RuntimeException and throw that.
3) Create 3 threads. Ensure that 2nd thread starts only after the 1st thread ends and the 3rd starts
only after the 2nd thread ends using the join method. Each thread should print its start and end
along with its name.
public class Symbols extends Thread {
private final char symbol;
}
}
TrafficThread(String light) {
this.light = light;
}
@Override
public void run() {
System.out.println(TrafficEnum.valueOf(light).getAction());
}
}
}
}
// Stop
// turn on the engine
// You can goooooo
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
And lets see an example where we shutdown the executor within 10 seconds regardless of weather
executor fully executed or not.
public class Symbols extends Thread {
private final char symbol;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class FutureDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService service = Executors.newFixedThreadPool(2);
Name task1 = new Name("hemanth");
Name task2 = new Name("Java");
Name task3 = new Name("KG Coding");
Future<String> name1 = service.submit(task1);
Future<String> name2 = service.submit(task2);
Future<String> name3 = service.submit(task3);
System.out.println("\n" + name1.get());
System.out.println("\n" + name2.get());
System.out.println("\n" + name3.get());
service.shutdown();
}
}
CHALLENGES
1) Write a program that creates a single-threaded executor service. Define and submit a simple
Runnable task that prints numbers from 1-10. After submission, shutdown the executor.
public class Print1210 implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println(i);
}
}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 8
// 9
// 10
OR
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
3) Write a program that uses an executor service to execute multiple tasks. Each task should calculate
and return the factorial of a number provided to it. Use future objects to receive the results of the
calculations. After all tasks are submitted, retrieve the results from the future, print them, and ensure
the executor service is shutdown correctly.
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
// 3628800
// 362880
// 120
OR
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
// 1
// 2
// 6
// 24
// 120
// 720
// 5040
// 40320
// 362880
// 3628800
KEY POINTS
1. Main thread is the only user created thread that is created automatically when a program starts.
2. run() is automatically called when thread is started using the start().
3. A thread can not be in both runnable and running states.
4. join() methods causes the calling thread to stop execution until the thread it joins with, stops
running.
5. Synchronized keyword prevents 2 threads from executing a method simultaneously.
6. notify() wakes up single thread, notifyAll() wakes up all the threads.
7. Executor service must be explicitly shutdown to terminate the threads it manages.
8. Thread.yield() is a static method in Java that hints to the thread scheduler that the current
thread is willing to pause its execution temporarily, allowing other threads of the same or higher
priority a chance to execute.