0% found this document useful (0 votes)
5 views18 pages

Multi Threading

The document outlines a series of tasks involving file processing and thread management in Java, comparing single-threaded and multithreaded approaches. It demonstrates how synchronization and semaphores can prevent race conditions and ensure correct data handling when multiple threads access shared resources. Observations highlight the importance of using these techniques to maintain data integrity and control concurrency in multithreaded applications.

Uploaded by

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

Multi Threading

The document outlines a series of tasks involving file processing and thread management in Java, comparing single-threaded and multithreaded approaches. It demonstrates how synchronization and semaphores can prevent race conditions and ensure correct data handling when multiple threads access shared resources. Observations highlight the importance of using these techniques to maintain data integrity and control concurrency in multithreaded applications.

Uploaded by

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

Submitted by

Akalya E
Task - 1
● Manually create 5 text files with large amounts of data with more than 10 K lines.

● Read the files and: Calculate the line count for each file

● Measure the time taken to process the files

● Execute this workflow using: Single-threaded approach, Multithreaded approach

● Compare the execution times and document your observations.

Single thread

● It executes tasks one after another using a single thread.


● Only one task runs at a time.
● It is simple but slower for large workloads.
● The execution order is fixed and predictable.

Multithread

● It executes multiple tasks at the same time using multiple threads.


● Tasks run in parallel and independently.
● It improves performance and reduces execution time.
● The execution order is not fixed due to thread scheduling

Thread creation

Using Thread class

● Create a class that extends Thread


● Override the run() method (this contains the task)
● Call start() to begin execution

Using Runnable interface

● Create a class that implements Runnable


● Write task inside run()
● Pass it to Thread object and call start()

FileCreation

import [Link];
import [Link];
public class FileCreation {
public static void main(String[] args) throws IOException {
for (int i = 1; i <= 5; i++) {
FileWriter fw = new FileWriter("file" + i + ".txt");
for (int j = 1; j <= 15000; j++) {
[Link]("Processing record " + j + " of file " + i + "\n");
} [Link]();
[Link]("Files created successfully!");
}
}

Single thread
import [Link].*;
public class SingleThread {
public static void main(String[] args) throws Exception {
long start = [Link]();
for (int i = 1; i <= 5; i++) {
BufferedReader br = new BufferedReader(new FileReader("file" + i + ".txt"));
int count = 0;
while ([Link]() != null) {
count++;
}
[Link]();
[Link]("File " + i + " lines: " + count);
}
long end = [Link]();
[Link]("Single Thread Time: " + (end - start) + " ms");
}
}

Multithread
import [Link].*;
class FileTask implements Runnable {
String fileName;
FileTask(String fileName) {
[Link] = fileName;
}
public void run() {
try {
BufferedReader br = new BufferedReader(new FileReader(fileName));
int count = 0;
while ([Link]() != null) {
count++;
} [Link]();
[Link](fileName + " lines: " + count);
} catch (Exception e) {
[Link]();
}
}
}
public class Multithread {
public static void main(String[] args) throws Exception {
long start = [Link]();
Thread t1 = new Thread(new FileTask("[Link]"));
Thread t2 = new Thread(new FileTask("[Link]"));
Thread t3 = new Thread(new FileTask("[Link]"));
Thread t4 = new Thread(new FileTask("[Link]"));
Thread t5 = new Thread(new FileTask("[Link]"));
[Link]();
[Link]();
[Link]();
[Link]();
[Link]();
[Link]();
[Link]();
[Link]();
[Link]();
[Link]();
long end = [Link]();
[Link]("Multi Thread Time: " + (end - start) + " ms");
}
}
Output screenshots
Files created successfully

Singlethread execution
Files processed sequentially (one after another)
Total time taken=79ms
● The single-threaded approach processes files one after another, so it takes more time
due to sequential execution.
● The multithreaded approach processes multiple files at the same time, which reduces
total execution time.
● In this case, multithreading performed faster (58 ms) compared to single-threading
(79 ms) because tasks were executed in parallel.

Multithread execution
Files processed in parallel using multiple threads
Total time taken=58ms

Approach Execution Style Time Taken ( result)

Single-threaded Sequential processing 79 ms

Multithreaded Parallel processing 58 ms


Task - 2
● Consider a group of 1000 workers working on a project with budget of 10000000.
● Due to project expenses, each worker withdraws 1000 from the budget account
simultaneously.
● After all workers have completed their withdrawals: What should be the final
balance?
● Validate whether the computed balance is correct.
● If there is any discrepancy, identify and explain the cause.
Synchronization
● Synchronization is a mechanism used to control access to shared resources by multiple
threads.
● It ensures that only one thread can access a critical section at a time.
● It is used to prevent race conditions and maintain data consistency.
● In Java, synchronization can be achieved using the synchronized keyword.
● When a method or block is synchronized, other threads must wait until the current
thread completes execution.
Code
Account
class Account {
int balance = 10000000;
synchronized void withdraw(int amount) {
balance = balance - amount;
}
}
Employee
class Employee extends Thread {
Account acc;
Employee(Account acc) {
[Link] = acc;
}
public void run() {
[Link](1000);
}}
Main
public class Main {
public static void main(String[] args) throws Exception {
Account acc = new Account();
Employee[] employees = new Employee[1000];
for (int i = 0; i < 1000; i++) {
employees[i] = new Employee(acc);
employees[i].start();
}
for (int i = 0; i < 1000; i++) {
employees[i].join();
}
[Link]("Balance is : " + [Link]);
}
}
Output
Using synchronization
With synchronization, the final balance is correctly reduced to 9000000 because only one
thread updates the shared resource at a time.
Without synchronization
Without synchronization, the program exhibits race conditions where multiple threads access
and modify the shared balance simultaneously. This leads to lost updates, resulting in an
incorrect final balance such as 9001000 instead of the expected 9,000,000.

Task-3

● Simulate 10 cars attempting to park in a parking area using 10 threads executed via
ExecutorService.
● Maintain a global counter to track the total number of cars parked (updated by each
Runnable task).
● After all car threads have completed execution, print the final counter value.
● Enhance the implementation by:
● Introducing synchronization to ensure thread-safe updates to the shared counter
● Using a Semaphore (slot count = 3) to limit the number of cars that can park
simultaneously

● Finally, compare the behavior and results:


● Without synchronization vs with synchronization
● Without semaphore vs with semaphore
● Document your observations.

ExecutorService
● ExecutorService is a framework used to manage and control multiple threads
efficiently.
● It provides a thread pool, so threads are reused instead of creating new ones every time.
● It simplifies multithreading by handling thread creation, execution, and termination.
● Tasks are submitted using methods like execute() or submit().
● It improves performance and scalability in applications with many tasks.
● Common types include FixedThreadPool, CachedThreadPool, and
SingleThreadExecutor

Semaphore
● Semaphore is a synchronization tool used to control the number of threads accessing a
shared resource.
● It works using permits (like tokens or slots).
● A thread must acquire a permit before accessing the resource and release it after use.
● It is used to limit concurrency, not to protect data directly.

Semaphore use two functions


● acquire()- Request access
● release()-Release resource

Semaphore creation
Semaphore sem = new Semaphore(3);
creating a Semaphore object that allows maximum 3 threads at the same time.
Code i. Semaphore +Synchronization
import [Link].*;
public class Main {
private static int parkedCars = 0;
private static final Object lock = new Object();
private static final Semaphore parkingSlots = new Semaphore(3);
public static void main(String[] args) {
ExecutorService executor = [Link](10);
for (int i = 1; i <= 10; i++) {
int carId = i;
[Link](() -> {
try {
[Link]("Car " + carId + " -> waiting for slot");
[Link]();
[Link]("Car " + carId + " -> ENTERED parking");
[Link](500);
synchronized (lock) {
parkedCars++;
[Link]("Car " + carId + " -> PARKED | Total = " + parkedCars);
}
[Link]("Car " + carId + " -> EXITING parking");
[Link]();
} catch (InterruptedException e) {
[Link]().interrupt();}
});
}
[Link]();
try {
[Link](1, [Link]);
} catch (InterruptedException e) {
[Link]().interrupt();
}
[Link]("\nFINAL PARKED COUNT = " + parkedCars);
}}
Output
Only 3 cars enter at a time due to semaphore, and synchronization ensures correct counting,
so final parked count = 10.
Code ii. Without synchronization
import [Link].*;
public class Main {
private static int parkedCars = 0;
private static final Object lock = new Object();
private static final Semaphore parkingSlots = new Semaphore(3);
public static void main(String[] args) {
ExecutorService executor = [Link](10);
for (int i = 1; i <= 10; i++) {
int carId = i;
[Link](() -> {
try {
[Link]("Car " + carId + " -> waiting for slot");
[Link]();
[Link]("Car " + carId + " -> ENTERED parking");
[Link](500);
parkedCars++;
[Link]("Car " + carId + " -> PARKED | Total = " + parkedCars);
[Link]("Car " + carId + " -> EXITING parking");
[Link]();
} catch (InterruptedException e) {
[Link]().interrupt();
}
});
}
[Link]();
try {
[Link](1, [Link]);
} catch (InterruptedException e) {
[Link]().interrupt();
}
[Link]("\nFINAL PARKED COUNT = " + parkedCars);
}
}
Output
Semaphore limits cars to 3 at a time, but lack of synchronization causes race conditions,
leading to incorrect final count
Code iii. Without Semaphore
import [Link].*;
public class Main {
private static int parkedCars = 0;
private static final Object lock = new Object();
public static void main(String[] args) {
ExecutorService executor = [Link](10);
for (int i = 1; i <= 10; i++) {
int carId = i;
[Link](() -> {
try {
[Link]("Car " + carId + " -> waiting for slot");
[Link]("Car " + carId + " -> ENTERED parking");
[Link](500);
synchronized (lock) {
parkedCars++;
[Link]("Car " + carId + " -> PARKED | Total = " + parkedCars);
}
[Link]("Car " + carId + " -> EXITING parking");
} catch (InterruptedException e) {
[Link]().interrupt();
}
});
} [Link]();
try {
[Link](1, [Link]);
} catch (InterruptedException e) {
[Link]().interrupt();
}
[Link]("\nFINAL PARKED COUNT = " + parkedCars);}}
Output
All cars enter parking simultaneously without any limit, but synchronization maintains
correct final count = 10
Code iv. Without synchronization and semaphore
import [Link].*;
public class Main {
private static int parkedCars = 0;
private static final Object lock = new Object();
public static void main(String[] args) {
ExecutorService executor = [Link](10);
for (int i = 1; i <= 10; i++) {
int carId = i;
[Link](() -> {
try {
[Link]("Car " + carId + " -> waiting for slot");
[Link]("Car " + carId + " -> ENTERED parking");
[Link](500);
parkedCars++;
[Link]("Car " + carId + " -> PARKED | Total = " + parkedCars);
[Link]("Car " + carId + " -> EXITING parking");
} catch (InterruptedException e) {
[Link]().interrupt();
}
});
} [Link]();
try {
[Link](1, [Link]);
} catch (InterruptedException e) {
[Link]().interrupt();
}
[Link]("\nFINAL PARKED COUNT = " + parkedCars);}}
Output
All cars enter at once and race conditions occur, resulting in uncontrolled access and
incorrect final count.
Observation - Task3:

● When both Semaphore and Synchronization are used, only a limited number of cars
(3) enter at a time, and the final parked count is correct (10).
● When Synchronization is removed, even though the semaphore controls entry, the
shared counter is updated incorrectly due to race conditions, resulting in a wrong final
count.
● When Semaphore is removed, all cars enter the parking area at the same time (no
limit), but synchronization ensures the final count is still correct.
● When both Semaphore and Synchronization are removed, there is no control over
entry and no protection of shared data, leading to incorrect results and unpredictable
behavior.

You might also like