0% found this document useful (0 votes)
7 views40 pages

Multithreading in Java

Uploaded by

busipallibalaji
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views40 pages

Multithreading in Java

Uploaded by

busipallibalaji
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd

Multithreading in Java

Topics Covered: Multithreading definition, Concurrency vs Parallelism, Thread creation,


Thread lifecycle, join/sleep/yield, naming/priority/daemon, race condition &
synchronization, deadlock, inter-thread communication, thread pool, Executor framework,
Callable/Future, CompletableFuture.
What is Multithreading?
Definition: Multithreading in Java refers to the ability to execute multiple threads
concurrently within a single program.
A thread is the smallest unit of a process. Using multithreading, a program can perform several tasks
at the same time, leading to better performance and responsiveness.
Concurrency vs Parallelism
Concurrency: Multiple tasks make progress over the same period of time, but not
necessarily at the exact same instant (like a single chef juggling tasks).
Parallelism: Multiple tasks execute truly at the same time on different processor cores (like multiple
chefs cooking simultaneously).
Main Thread
Every Java program starts with a single thread known as the main thread.
It is the entry point of the program and can create other threads.
Creating Threads in Java
There are two primary ways to create a thread:
1) Extending the Thread class
2) Implementing the Runnable interface
Important: Use start() to create a new thread. Calling run() directly executes in the current thread.
Method 1: Extending Thread class
Steps:
• Create a class that extends [Link]
• Override run() with the code to execute
• Create object and call start()
Notes: start() allocates a new thread and calls run(). Calling start() twice causes
IllegalThreadStateException.
Example: Extending Thread class
package [Link];

public class ThreadCreationTest extends Thread {

@Override
public void run() {
[Link]("this is run method " + [Link]().getName());
}

public static void main(String[] args) {


ThreadCreationTest t = new ThreadCreationTest();
[Link](); // creates the child thread-0
// [Link](); //IllegalThreadStateException

ThreadCreationTest t1 = new ThreadCreationTest();


[Link](); // creates the child thread-1

ThreadCreationTest t2 = new ThreadCreationTest();


[Link](); // creates the child thread-2

[Link]("this is main method: " + [Link]().getName());


}
}
Method 2: Implementing Runnable
Steps:
• Create a class that implements [Link]
• Implement run() method
• Create a Thread object passing Runnable instance
• Call start() on the Thread object
Example: Runnable
package [Link];

public class ThreadTest implements Runnable {


@Override
public void run() {
for (int i = 0; i < 10; i++) {
[Link]("Child Thread.." + [Link]().getName());
}
}

public static void main(String[] args) {


ThreadTest test = new ThreadTest();

Thread t = new Thread(test);


[Link]();
// [Link] IllegalThreadStateException

Thread t1 = new Thread(test);


[Link]();

for (int i = 0; i < 5; i++) {


[Link]("main Thread..." + [Link]().getName());
}
}
}
Thread Lifecycle
1) New Born: Thread created but start() not called
2) Runnable: start() called; ready to run
3) Running: Scheduler selected thread; executing
4) Blocked/Waiting: Waiting for resource/lock/I/O
5) Terminated/Dead: Finished run() or stopped due to exception
Thread Control Methods
Java provides important thread control methods:
• join() • sleep() • yield()
join() Method
join() causes the currently running thread to wait until the thread on which join() is called
has finished execution.
Example meaning: main thread waits for child thread to die before continuing.
join() Example Program
package [Link];

public class ThreadControll extends Thread {


@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
[Link](2000);
} catch (InterruptedException e) {
[Link]();
}
[Link]("run method...." + [Link]().getName());
}
}

public static void main(String[] args) throws InterruptedException {


ThreadControll c = new ThreadControll();
[Link]();
[Link]();
[Link]("main method...." + [Link]().getName());
}
}
sleep() Method
sleep() pauses the currently executing thread for a specified time.
After the time elapses, the thread returns to Runnable state.
Key points: sleep() is static; affects current thread; can throw InterruptedException.
sleep() Example Program
package [Link];

public class ThreadControll extends Thread {


@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
[Link](5000); // Thread goes to sleep for 5 sec
} catch (InterruptedException e) {
[Link]();
}
[Link]("run method...." + [Link]().getName());
}
}

public static void main(String[] args) {


ThreadControll c = new ThreadControll();
[Link]();
[Link]("main method...." + [Link]().getName());
}
}
yield() Method
yield() hints the scheduler that the current thread is willing to pause to allow other threads
of same or higher priority to run.
It moves the current thread from Running to Runnable.
Key points: static method; no guarantee; default priority is 5; priority range 1–10.
yield() Example Program
class MyThread extends Thread {

public void run() {


for (int i = 1; i <= 5; i++) {
[Link]([Link]().getName() + " is running: " + i);
[Link]();
}
}
}

public class YieldExample {


public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();

[Link]("Thread-A");
[Link]("Thread-B");

[Link]();
[Link]();
}
}
Yield vs Join
Yield: Hint to scheduler; Static method; Running→Runnable; No guarantee; No checked
exception.
Join: Waits for specific thread; Instance method; Running→Waiting; Guaranteed; Throws
InterruptedException.
Naming a Thread
By default, Java names threads like Thread-0, Thread-1.
Custom names are recommended for debugging/logging.
setName(): Thread t1 = new Thread(); [Link]("Payment-Processor");
Get current name: [Link]().getName();
Thread Priority
Thread priority informs scheduler which threads are more important.
Range: 1 to 10
MIN_PRIORITY=1, NORM_PRIORITY=5 (default), MAX_PRIORITY=10
Example: [Link](Thread.MAX_PRIORITY);
Daemon Threads
A daemon thread is a low-priority background thread that provides services to user threads
(e.g., Garbage Collection).
JVM exits automatically if only daemon threads are running.
Must call setDaemon(true) before start(); else IllegalThreadStateException.
Race Condition Example
package [Link];

class MainTest {

int count = 0;
public void increment() {
count++;
}

public static void main(String[] args) throws InterruptedException {


MainTest test = new MainTest();

Thread t = new Thread(() -> {


for (int i = 0; i < 1000; i++) [Link]();
});

Thread t1 = new Thread(() -> {


for (int i = 0; i < 1000; i++) [Link]();
});

[Link]();
[Link]();

[Link]();
[Link]();

[Link]([Link]);
}
Race Condition (Explanation)
Output is unpredictable because two threads modify shared data at the same time.
increment() internally does: Read count → Add 1 → Write back.
When two threads read the same value and update, one update is lost.
This problem is called Race Condition and can be avoided using synchronized.
Thread Synchronization
Synchronization controls access to shared resources to prevent race condition and data
inconsistency.
Only one thread can execute critical code at a time (prevents thread interference).
synchronized keyword can be used for synchronized methods or synchronized blocks.
Synchronized Method Example
package [Link];

public class MainTest {

int count = 0;

public synchronized void increment() {


count++;
}

public static void main(String[] args) throws InterruptedException {


MainTest test = new MainTest();

Thread t = new Thread(() -> {


for (int i = 0; i < 1000; i++) [Link]();
});

Thread t1 = new Thread(() -> {


for (int i = 0; i < 1000; i++) [Link]();
});

[Link]();
[Link]();

[Link]();
[Link]();

[Link]([Link]); //2000
Synchronized Block Example

public class MainTest {


int count = 0;
int count1 = 0;

public void increment() {


synchronized (this) {
count++; // locks only this portion
}
count1++; // can be executed by multiple threads
}
}
Synchronized Method vs Block
Synchronized Method: Locks entire method; less efficient; less control; easy to write.
Synchronized Block: Locks only required code; more efficient; more control; best practice.
AtomicInteger

import [Link];

class MainTest {

AtomicInteger count = new AtomicInteger(0);

public void increment() {


[Link]();
}
}
Deadlock (Definition)
Deadlock happens when two or more threads get stuck forever because each thread is
waiting for a lock that another thread holds.
Example: Person A has Key 1 needs Key 2; Person B has Key 2 needs Key 1.
package [Link];

Deadlock Example Program


public class DeadLockTest {

public static void main(String[] args) {

DeadLockTest test = new DeadLockTest();


DeadLockTest test1 = new DeadLockTest();

Thread t1 = new Thread(() -> {


synchronized (test) {
[Link]("Thread 1 got lock1");
try { [Link](100); } catch (Exception e) {}
synchronized (test1) {
[Link]("Thread 1 got lock2");
}
}
});

Thread t2 = new Thread(() -> {


synchronized (test1) {
[Link]("Thread 2 got lock2");
try { [Link](100); } catch (Exception e) {}
synchronized (test) {
[Link]("Thread 2 got lock1");
}
}
});

[Link]();
Avoid Deadlock
How to avoid deadlock:
• Always acquire locks in the same order
• Avoid nested locks if possible
• Use tryLock() instead of synchronized
• Keep synchronized code small
Inter-Thread Communication
Inter-thread communication allows threads to coordinate their work.
Java provides 3 methods in Object class: wait(), notify(), notifyAll().
Must be used inside synchronized.
wait(): thread waits and releases lock; notify(): wakes one waiting thread; notifyAll(): wakes all.
Producer-Consumer Shared Class
public class Data {
int value;
boolean ready = false;

synchronized void produce(int v) {


if (ready) {
try { wait(); } catch (Exception e) {}
}
value = v;
ready = true;
[Link]("Produced: " + value);
notify();
}

synchronized void consume() {


if (!ready) {
try { wait(); } catch (Exception e) {}
}
[Link]("Consumed: " + value);
ready = false;
notify();
}
}
Producer & Consumer Threads
public class Producer extends Thread {
Data d;
Producer(Data d) { this.d = d; }

public void run() { [Link](10); }


}

public class Consumer extends Thread {


Data d;
Consumer(Data d) { this.d = d; }

public void run() { [Link](); }


}

public class Test {


public static void main(String[] args) {
Data d = new Data();
new Producer(d).start();
new Consumer(d).start();
}
}
ThreadPool
Creating a thread is expensive. Creating new threads repeatedly degrades performance.
A thread pool reduces overhead by reusing threads.
Threads stay in the pool, pick tasks from a queue (BlockingQueue), execute, and then take new tasks.
Executors Framework
Executors framework helps with: Thread creation, Thread management, task
submission/execution.
Executor (I): execute(Runnable).
ExecutorService (I): manages lifecycle and supports submit(Runnable/Callable).
Executors class provides factory methods like newFixedThreadPool() and newCachedThreadPool().
ThreadPool Executor Example
package [Link];
import [Link];
import [Link];

public class ThreadPoolExecutorTest {


public static void main(String[] args) {
ExecutorService threads = [Link](10);

[Link](new Runnable() {
@Override
public void run() {
[Link]("child thread1.." + [Link]().getName());
}
});

[Link](() -> {
[Link]("child thread2.." + [Link]().getName());
});

[Link](() -> {
[Link]("child thread3.." + [Link]().getName());
});

[Link](() -> {
[Link]("child thread4.." + [Link]().getName());
});

[Link]("main method.." + [Link]().getName());


Callable & Future (Concept)
Runnable tasks cannot return results.
Callable (Java 1.5) can return a value and throw checked exception.
Submitting a Callable using [Link]() returns a Future object.
Future is used to check task status and retrieve returned value.
Callable & Future Example

import [Link];
import [Link];

Future<String> submit = [Link](() -> {


[Link]("submit(callable) child thread8.." + [Link]().getName());
return "task completed using lamda expression ";
});

[Link]([Link]());
[Link]([Link]());
[Link]([Link]());
CompletableFuture Example
package [Link];
import [Link];

public class CompletableFutureTest {


public static void main(String[] args) {

[Link](() -> {
[Link]("task submitted using runasync with runnable parameter."
+ [Link]().getName());
});

CompletableFuture<String> supplyAsync1 = [Link](() -> {


return "task1 submitted using supplyAsync with supplier parameter."
+ [Link]().getName();
});

CompletableFuture<String> supplyAsync2 = [Link](() -> {


return "task2 submitted using supplyAsync with supplier parameter."
+ [Link]().getName();
});

CompletableFuture<String> thenCombine =
[Link](supplyAsync2, (res1, res2) -> res1 + " " + res2);

[Link]([Link]());

CompletableFuture<String> completedFuture =
[Link]("forcefully completed...");

You might also like