1 Commits
Author SHA1 Message Date
emad ae49575bd9 implement TODOs exept bonus tasks 2026-06-05 21:14:39 +04:30
5 changed files with 81 additions and 209 deletions
-202
View File
@@ -1,202 +0,0 @@
# Eighth Assignment: Multithreading Basics
## Table of contents
- [Introduction](#introduction)
- [Objectives 🎯](#objectives-)
- [Theoretical Questions 📝](#theoretical-questions-)
- [Practical Questions 💻](#practical-questions-)
- [Evaluation ⚖️](#evaluation-)
- [Submission ⌛](#submission-)
- [Additional Resources 📚](#additional-resources-)
## Important Note:
This project is configured as a **Maven project**. If you're opening this project in an IDE (like IntelliJ IDEA or VS Code), please ensure you import it as a Maven project so that dependencies and build settings are recognized automatically.
To run the project from the command line, use:
```bash
mvn compile
mvn exec:java -Dexec.mainClass="Main"
```
## Introduction
Welcome to your Eighth Advanced Programming (AP) Assignment. This project is divided into two main sections:
1. **Theoretical Questions**: Analyze key multithreading concepts (Start vs Run, Daemon threads, and Lambdas).
2. **Practical Questions**: Implement a **Simulated Download Manager**. You will use Java Threads to simulate downloading a file in multiple chunks concurrently.
> **⚠️ Note:** This is a **local simulation only**. There is no actual network activity, URL connection, or socket programming involved. The goal is to practice thread management and state observation.
## Objectives 🎯
By completing this assignment, you will:
- Apply **multithreading** basics using the `Thread` class and `Runnable` interface.
- Understand how to manage multiple worker threads performing independent tasks.
- Implement a monitor thread to observe the progress of other threads.
- Practice using `start()` and `join()` for thread lifecycle management.
## Theoretical Questions 📝
**Note: Please answer these questions in a Markdown file (Report.md) and place it in the root directory of your fork. Include code or screenshots where you see fit.**
### 1. `start()` vs `run()`
```java
public class StartVsRun {
static class MyRunnable implements Runnable {
public void run() {
System.out.println("Running in: " + Thread.currentThread().getName());
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new MyRunnable(), "Thread-1");
System.out.println("Calling run()");
t1.run();
Thread.sleep(100);
Thread t2 = new Thread(new MyRunnable(), "Thread-2");
System.out.println("Calling start()");
t2.start();
}
}
```
**Questions:**
- What output do you get from the program? Why?
- Whats the difference in behavior between calling `start()` and `run()`?
---
### 2. Daemon Threads
```java
public class DaemonExample {
static class DaemonRunnable implements Runnable {
public void run() {
for(int i = 0; i < 20; i++) {
System.out.println("Daemon thread running...");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
//[Handling Exception...]
}
}
}
}
public static void main(String[] args) {
Thread thread = new Thread(new DaemonRunnable());
thread.setDaemon(true);
thread.start();
System.out.println("Main thread ends.");
}
}
```
**Questions:**
- What output do you get from the program? Why?
- What happens if you remove `thread.setDaemon(true)`?
- What are some real-life use cases of daemon threads?
---
### 3. A shorter way to create threads
```java
public class ThreadDemo {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
System.out.println("Thread is running using a ...!");
});
thread.start();
}
}
```
**Questions:**
- What output do you get from the program?
- What is the `() -> { ... }` syntax called?
- How is this code different from creating a class that extends `Thread` or implements `Runnable`?
## Practical Questions 💻
### Simulated Download Manager
You are tasked with completing a skeleton project for a download manager. The application reads a configuration file, splits a "file" into several chunks, and assigns each chunk to a dedicated worker thread.
#### 🏗 Project Structure
- `src/main/resources/download_config.txt`: Contains simulation parameters (file size, chunk count, delays).
- `DownloadWorker.java`: The logic for simulating a chunk download (needs implementation).
- `ProgressMonitor.java`: A thread that periodically prints the total progress (needs implementation).
- `Main.java`: The entry point that initializes chunks, starts threads, and waits for completion.
- `ChunkStatus.java`: Data class holding the state of individual chunks.
#### 🛠 What You Need to Do
In the provided source code, look for **`// TODO`** comments. You must:
1. **Implement `DownloadWorker`**:
- Record start/end times for each chunk.
- Use a loop to simulate progress based on the random delays and step sizes provided in the config.
- Update the shared `ChunkStatus` object so the monitor can see progress.
2. **Implement `ProgressMonitor`**:
- Periodically calculate the total downloaded megabytes across all chunks.
- Exit gracefully once all chunks are marked as completed.
3. **Complete `Main`**:
- Properly instantiate and `start()` the worker threads and the monitor thread.
- Use `join()` to ensure the main thread waits for all workers to finish before printing the final report.
#### ⚙️ Configuration
The simulation behavior is controlled by `src/main/resources/download_config.txt`. You can modify these values to test different scenarios (e.g., more chunks or faster/slower speeds).
---
## Bonus Tasks 🌟
- Download Speed and ETA:
- Calculate and display the current overall download speed during the simulation.
- Estimate and show the remaining time (ETA) based on the current progress and speed.
- User Interface (UI):
- Improve the console output or design a simple UI that presents download progress in a cleaner and more intuitive way.
- Make the simulation easier to follow by showing chunk activity, total progress, and final results in a user-friendly format.
- Real-Time Progress Bar:
- Implement a real-time progress bar that updates in place instead of printing a new line each time.
- Show smooth progress growth in the console so the output looks more like a real download manager.
- Sequential vs Multithreaded Comparison:
- Add a mode to simulate downloading chunks sequentially (one after another) and compare it with the multithreaded version.
- Measure and report the total execution time of both approaches, and briefly analyze the difference in performance.
## Evaluation ⚖️
Your work will be evaluated based on:
- **Thread Management**: Correct use of `start()` and `join()`.
- **Simulation Logic**: Correct implementation of the loops and random delays in the worker threads.
- **Thread Safety**: Following the constraint of each worker only writing to its own assigned object.
- **Code Quality**: Readable code and proper use of Java conventions.
**Total: 500 points**
- 🧠 Theoretical Questions 150 points
- 💻 Practical Task (Download Manager) 350 points
## Submission ⌛
1. Add your mentor as a contributor to the project.
2. Create a `develop` branch for implementing features.
3. Use Git for regular code commits.
4. Push your code and the answers file (Report.md) to the remote repository.
5. Submit a pull request to merge the `develop` branch with `main`.
**Deadline:** **Friday, June 5** (15th of Khordad)
## Additional Resources 📚
- [Java Concurrency and Multithreading](https://jenkov.com/tutorials/java-concurrency/index.html)
- [Creating and Starting Java Threads](https://jenkov.com/tutorials/java-concurrency/creating-and-starting-threads.html)
- [Thread.join() explained](https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#join--)
+39
View File
@@ -0,0 +1,39 @@
# Answers of theoretical questions
## 1:
**outputs:**
Calling run()
Running in: main
Calling start()
Running in: Thread-2
this is because of the difference between run() and start() method.
when you call run method, it only calls the run function of StartVsRun class and it runs on the main thread;
but when you call **start** method it runs on new thread(here we name it Thread-2)
---
## 2:
**outputs:**
Main thread ends.
Daemon thread running...
if we set daemon true The **JVM** terminates the thread right after the main thread ends.
if we don't set the thread as a daemon, after the main thread ended it will continue it's work until it will be done(print Daemon thread running 20 times).
### Real-Life use case of daemon threads:
- Memory and Resource Management(like java's garbage collector)
- Infrastructure & Telemetry Monitoring(like send a ping to a central server every 5 seconds)
- Spell Checkers
## 3:
**output:**
Thread is running using a ...!
it calls **Lambda Expression** and it is shorter and faster way than making a class which extend thread or implement runnable to have another thread for our works.
+18 -2
View File
@@ -23,21 +23,37 @@ public class DownloadWorker implements Runnable {
public void run() { public void run() {
// TODO: Record the chunk start time in chunkStatus. // TODO: Record the chunk start time in chunkStatus.
double downloaded = 0.0; double downloaded = 0.0;
chunkStatus.setStartTimeMs(System.currentTimeMillis());
// TODO: Print a message that this chunk has started downloading. // TODO: Print a message that this chunk has started downloading.
System.out.println("Chunk " + chunkStatus.getChunkId() + " has started downloading.");
while (downloaded < chunkStatus.getChunkSizeMB()) { while (downloaded < chunkStatus.getChunkSizeMB()) {
// TODO: Generate a random sleep delay between min and max delay. // TODO: Generate a random sleep delay between min and max delay.
// TODO: Sleep for that delay. // TODO: Sleep for that delay.
try {
Thread.sleep(random.nextInt(config.getMinStepDelayMs(), config.getMaxStepDelayMs()));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// TODO: Generate a random download amount for this step. // TODO: Generate a random download amount for this step.
// TODO: Increase downloaded, but do not go beyond chunk size. // TODO: Increase downloaded, but do not go beyond chunk size.
downloaded += random.nextDouble(config.getMinStepDownloadMB(), config.getMaxStepDownloadMB());
if(downloaded > chunkStatus.getChunkSizeMB())
{
downloaded = chunkStatus.getChunkSizeMB();
}
// TODO: Save the updated downloaded value into chunkStatus. // TODO: Save the updated downloaded value into chunkStatus.
chunkStatus.setDownloadedMB(downloaded);
// TODO: Optionally print step-by-step progress. // TODO: Optionally print step-by-step progress.
System.out.println(chunkStatus);
} }
// TODO: Mark the chunk as completed. // TODO: Mark the chunk as completed.
chunkStatus.setCompleted(true);
// TODO: Record the chunk end time in chunkStatus. // TODO: Record the chunk end time in chunkStatus.
chunkStatus.setEndTimeMs(System.currentTimeMillis());
// TODO: Print a message that this chunk has finished downloading. // TODO: Print a message that this chunk has finished downloading.
System.out.println("Chunk " + chunkStatus.getChunkId() + " has finished downloading.");
} }
} }
+18 -5
View File
@@ -37,6 +37,7 @@ public class Main {
// TODO: // TODO:
// Students may print helpful debug information here, // Students may print helpful debug information here,
// for example which chunk is assigned to which worker thread. // for example which chunk is assigned to which worker thread.
System.out.println("Chunk " + chunk.getChunkId() + " assigned to " + workerThread.getName());
} }
// 4. Create and start monitor thread // 4. Create and start monitor thread
@@ -48,12 +49,16 @@ public class Main {
// so that progress can be displayed while downloading happens. // so that progress can be displayed while downloading happens.
// //
// Example idea: // Example idea:
// monitorThread.start(); monitorThread.start();
// 5. Start worker threads // 5. Start worker threads
// TODO: // TODO:
// Start each worker thread in workerThreads. // Start each worker thread in workerThreads.
// Use a loop and call start() on each thread. // Use a loop and call start() on each thread.
for(Thread worker: workerThreads)
{
worker.start();
}
// 6. Wait for workers to finish // 6. Wait for workers to finish
// TODO: // TODO:
@@ -61,9 +66,13 @@ public class Main {
// This should be done inside a try-catch block for InterruptedException. // This should be done inside a try-catch block for InterruptedException.
// //
// Hint: // Hint:
// for (Thread thread : workerThreads) { try {
// thread.join(); for (Thread thread : workerThreads) {
// } thread.join();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// TODO: // TODO:
// After all workers finish, the monitor thread may also need to stop. // After all workers finish, the monitor thread may also need to stop.
@@ -72,7 +81,11 @@ public class Main {
// - add a stopping mechanism in ProgressMonitor later. // - add a stopping mechanism in ProgressMonitor later.
// //
// If your monitor finishes automatically, you may join it here. // If your monitor finishes automatically, you may join it here.
try {
monitorThread.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// NOTE: // NOTE:
// this final report may show 0 progress because no worker has actually run yet. // this final report may show 0 progress because no worker has actually run yet.
// Until students complete the thread start/join TODOs above, // Until students complete the thread start/join TODOs above,
+6
View File
@@ -57,6 +57,12 @@ public class ProgressMonitor implements Runnable {
// TODO: // TODO:
// If all chunks are completed, print a final message and exit the loop // If all chunks are completed, print a final message and exit the loop
if(completedChunks == chunks.size())
{
System.out.println("file has finished downloading");
break;
}
try { try {
Thread.sleep(monitorDelayMs); Thread.sleep(monitorDelayMs);