Add Report.md #2

Merged
Meraj merged 2 commits from develop into main 2026-07-31 23:31:05 +00:00
4 changed files with 93 additions and 90 deletions
+43
View File
@@ -0,0 +1,43 @@
**Question 1**
1- This is the output for the program:
``` Text
Calling run()
Running in: main
Calling start()
Running in: Thread-2
```
2- `t1.run()` will run the method from the thread that it is called from, which is `main` here. It does not create any new thread.
<br />
calling `t2.start()` will create a new thread (which is named `thread-2` here).
<hr />
**Question 2**
1- This is the output for the program:
``` Text
Main thread ends.
```
This happens because when the `main` thread is done and there is no other `User` thread running, then all of the `Daemon` threads will terminate.
2- If you remove that line, the thread becomes a `User` thread (the default).
*Behavior*:
The program will print "Main thread ends."
However, the JVM will not exit.
It will keep running for approximately 10 seconds until the DaemonRunnable finishes all 20 iterations of its loop.
Only after the run() method of that thread completes will the JVM shut down.
3- Some real-life use cases of daemon threads are background tasks that need to be automatically closed after the main application shuts down like `garbage collectors` and `spell checkers` or `auto saves`.
<hr />
**Question 3**
1- This is the output for the program:
``` Text
Thread is running using a ...!
```
2- It is called a `lambda expression`.
3- This is much more concise. But lacks the maintainability which class method provides.
+22 -18
View File
@@ -1,12 +1,5 @@
import java.util.Random; import java.util.Random;
/**
* Simulates downloading a single chunk of a file.
*
* <p>This class is intentionally provided as a skeleton for students.
* The main multithreading and simulation logic should be completed
* in the run() method.</p>
*/
public class DownloadWorker implements Runnable { public class DownloadWorker implements Runnable {
private final ChunkStatus chunkStatus; private final ChunkStatus chunkStatus;
@@ -21,23 +14,34 @@ public class DownloadWorker implements Runnable {
@Override @Override
public void run() { public void run() {
// TODO: Record the chunk start time in chunkStatus. chunkStatus.setStartTimeMs(System.currentTimeMillis());
double downloaded = 0.0; double downloaded = 0.0;
// TODO: Print a message that this chunk has started downloading. System.out.println("[Worker-" + chunkStatus.getChunkId() + "] Started downloading chunk of size: " + String.format("%.2f", chunkStatus.getChunkSizeMB()) + " MB");
while (downloaded < chunkStatus.getChunkSizeMB()) { while (downloaded < chunkStatus.getChunkSizeMB()) {
// TODO: Generate a random sleep delay between min and max delay. try {
// TODO: Sleep for that delay. int delay = config.getMinStepDelayMs() + random.nextInt(config.getMaxStepDelayMs() - config.getMinStepDelayMs() + 1);
// TODO: Generate a random download amount for this step. Thread.sleep(delay);
// TODO: Increase downloaded, but do not go beyond chunk size.
// TODO: Save the updated downloaded value into chunkStatus. double stepDownload = config.getMinStepDownloadMB() + (random.nextDouble() * (config.getMaxStepDownloadMB() - config.getMinStepDownloadMB()));
// TODO: Optionally print step-by-step progress.
downloaded += stepDownload;
if (downloaded > chunkStatus.getChunkSizeMB()) {
downloaded = chunkStatus.getChunkSizeMB();
} }
// TODO: Mark the chunk as completed. chunkStatus.setDownloadedMB(downloaded);
// TODO: Record the chunk end time in chunkStatus.
// TODO: Print a message that this chunk has finished downloading. } catch (InterruptedException e) {
System.err.println("Worker-" + chunkStatus.getChunkId() + " was interrupted.");
return;
}
} }
chunkStatus.setCompleted(true);
chunkStatus.setEndTimeMs(System.currentTimeMillis());
System.out.println("[Worker-" + chunkStatus.getChunkId() + "] Finished downloading.");
}
} }
+19 -49
View File
@@ -5,7 +5,6 @@ public class Main {
public static void main(String[] args) { public static void main(String[] args) {
System.out.println("=== Simulated Download Manager ==="); System.out.println("=== Simulated Download Manager ===");
// 1. Read config
DownloadConfig config; DownloadConfig config;
try { try {
config = ConfigReader.readConfig("download_config.txt"); config = ConfigReader.readConfig("download_config.txt");
@@ -19,65 +18,38 @@ public class Main {
System.out.println("Chunk count: " + config.getChunkCount()); System.out.println("Chunk count: " + config.getChunkCount());
System.out.println(); System.out.println();
// 2. Create chunks
List<ChunkStatus> chunks = ChunkUtils.createChunks( List<ChunkStatus> chunks = ChunkUtils.createChunks(
config.getTotalSizeMB(), config.getTotalSizeMB(),
config.getChunkCount() config.getChunkCount()
); );
// 3. Create worker threads
List<Thread> workerThreads = new ArrayList<>(); List<Thread> workerThreads = new ArrayList<>();
for (ChunkStatus chunk : chunks) { for (ChunkStatus chunk : chunks) {
DownloadWorker worker = new DownloadWorker(chunk, config); DownloadWorker worker = new DownloadWorker(chunk, config);
Thread workerThread = new Thread(worker, "Worker-" + chunk.getChunkId()); Thread workerThread = new Thread(worker, "Worker-" + chunk.getChunkId());
workerThreads.add(workerThread); workerThreads.add(workerThread);
// TODO: System.out.println("Assigned Chunk " + chunk.getChunkId() + " to " + workerThread.getName());
// Students may print helpful debug information here,
// for example which chunk is assigned to which worker thread.
} }
// 4. Create and start monitor thread
ProgressMonitor monitor = new ProgressMonitor(config, chunks); ProgressMonitor monitor = new ProgressMonitor(config, chunks);
Thread monitorThread = new Thread(monitor, "Progress-Monitor"); Thread monitorThread = new Thread(monitor, "Progress-Monitor");
monitorThread.start();
// TODO: for (Thread thread : workerThreads) {
// Start the monitor thread before starting the workers thread.start();
// so that progress can be displayed while downloading happens. }
//
// Example idea:
// monitorThread.start();
// 5. Start worker threads try {
// TODO: for (Thread thread : workerThreads) {
// Start each worker thread in workerThreads. thread.join();
// Use a loop and call start() on each thread. }
monitorThread.join();
} catch (InterruptedException e) {
System.err.println("Main thread interrupted while waiting for workers.");
}
// 6. Wait for workers to finish
// TODO:
// Wait for all worker threads to complete by calling join().
// This should be done inside a try-catch block for InterruptedException.
//
// Hint:
// for (Thread thread : workerThreads) {
// thread.join();
// }
// TODO:
// After all workers finish, the monitor thread may also need to stop.
// Depending on how ProgressMonitor is implemented, students may:
// - wait for it to finish on its own, or
// - add a stopping mechanism in ProgressMonitor later.
//
// If your monitor finishes automatically, you may join it here.
// NOTE:
// this final report may show 0 progress because no worker has actually run yet.
// Until students complete the thread start/join TODOs above,
// 7. Print final report
System.out.println(); System.out.println();
System.out.println("=== Final Report ==="); System.out.println("=== Final Report ===");
@@ -86,22 +58,20 @@ public class Main {
for (ChunkStatus chunk : chunks) { for (ChunkStatus chunk : chunks) {
downloadedMB += chunk.getDownloadedMB(); downloadedMB += chunk.getDownloadedMB();
if (chunk.isCompleted()) { if (chunk.isCompleted()) {
completedChunks++; completedChunks++;
} }
System.out.println( System.out.printf("Chunk %d: %.2f/%.2f MB (Duration: %d ms)%n",
"Chunk " + chunk.getChunkId() chunk.getChunkId(),
+ ": " + chunk.getDownloadedMB() chunk.getDownloadedMB(),
+ "/" + chunk.getChunkSizeMB() chunk.getChunkSizeMB(),
+ " MB" chunk.getDownloadDurationMs());
);
} }
System.out.println(); System.out.println();
System.out.println("Completed chunks: " + completedChunks + "/" + chunks.size()); System.out.println("Completed chunks: " + completedChunks + "/" + chunks.size());
System.out.println("Downloaded total: " + downloadedMB + "/" + config.getTotalSizeMB() + " MB"); System.out.printf("Downloaded total: %.2f/%d MB%n", downloadedMB, config.getTotalSizeMB());
System.out.println("Simulation finished."); System.out.println("Simulation finished.");
} }
} }
+5 -19
View File
@@ -16,33 +16,18 @@ public class ProgressMonitor implements Runnable {
@Override @Override
public void run() { public void run() {
// TODO:
// Repeatedly check chunk progress until all chunks are completed.
// In each loop:
// 1. Read the downloaded size from every chunk
// 2. Add all downloaded amounts to totalDownloadedMB
// 3. Count completed chunks
// 4. Print a progress message
// 5. If completedChunks == chunks.size(), print a final monitor message and stop
// 6. Otherwise sleep for monitorDelayMs and continue
while (true) { while (true) {
double totalDownloadedMB = 0.0; double totalDownloadedMB = 0.0;
int completedChunks = 0; int completedChunks = 0;
for (ChunkStatus chunk : chunks) { for (ChunkStatus chunk : chunks) {
totalDownloadedMB += chunk.getDownloadedMB(); totalDownloadedMB += chunk.getDownloadedMB();
if (chunk.isCompleted()) { if (chunk.isCompleted()) {
completedChunks++; completedChunks++;
} }
} }
double percent = 0.0; double percent = (totalSizeMB > 0) ? (totalDownloadedMB * 100.0) / totalSizeMB : 100.0;
if (totalSizeMB > 0) {
percent = (totalDownloadedMB * 100.0) / totalSizeMB;
}
System.out.printf( System.out.printf(
"Progress for %s: %.1f/%.1f MB (%.2f%%), completed chunks: %d/%d%n", "Progress for %s: %.1f/%.1f MB (%.2f%%), completed chunks: %d/%d%n",
@@ -54,9 +39,10 @@ public class ProgressMonitor implements Runnable {
chunks.size() chunks.size()
); );
if (completedChunks == chunks.size()) {
// TODO: System.out.println("Monitor: All chunks completed. Shutting down monitor...");
// If all chunks are completed, print a final message and exit the loop break;
}
try { try {
Thread.sleep(monitorDelayMs); Thread.sleep(monitorDelayMs);