From 8d71e2814eeb206038d3921af448c3c368248aa3 Mon Sep 17 00:00:00 2001 From: ZahraSadatMirvakili Date: Wed, 3 Jun 2026 00:03:54 +0330 Subject: [PATCH 1/3] basic multithreading --- src/main/java/DownloadWorker.java | 43 +++++++++++++----- src/main/java/Main.java | 39 +++++++--------- src/main/java/ProgressMonitor.java | 73 ++++++++++++++++++++++++++---- 3 files changed, 111 insertions(+), 44 deletions(-) diff --git a/src/main/java/DownloadWorker.java b/src/main/java/DownloadWorker.java index 546de2d..ec573cc 100644 --- a/src/main/java/DownloadWorker.java +++ b/src/main/java/DownloadWorker.java @@ -21,23 +21,42 @@ public class DownloadWorker implements Runnable { @Override public void run() { - // TODO: Record the chunk start time in chunkStatus. - double downloaded = 0.0; + long startTime = System.currentTimeMillis(); + chunkStatus.setStartTimeMs(startTime); - // TODO: Print a message that this chunk has started downloading. + double downloaded = 0.0; + int chunkId = chunkStatus.getChunkId(); + double chunkSize = chunkStatus.getChunkSizeMB(); + + System.out.println("[ Worker - " + chunkId +" ] Started downloading chunk " + chunkId +" (" + chunkSize + ") "); while (downloaded < chunkStatus.getChunkSizeMB()) { - // TODO: Generate a random sleep delay between min and max delay. - // TODO: Sleep for that delay. - // TODO: Generate a random download amount for this step. - // TODO: Increase downloaded, but do not go beyond chunk size. - // TODO: Save the updated downloaded value into chunkStatus. - // TODO: Optionally print step-by-step progress. + int delayMs = config.getMinStepDelayMs() + random.nextInt(config.getMaxStepDelayMs()-config.getMinStepDelayMs()+1); + + try { + Thread.sleep(delayMs); + }catch (InterruptedException e){ + System.out.println("[ Worker - " + chunkId +" ] interrupted "); + return; + } + + double stepSize = config.getMinStepDownloadMB() + random.nextDouble() * (config.getMaxStepDownloadMB() - config.getMinStepDownloadMB()); + + downloaded = Math.min(chunkSize, downloaded + stepSize); + + chunkStatus.setDownloadedMB(downloaded); + + System.out.println("[ Worker - " + chunkId +" ] Chunk " + chunkId + ": " + Math.round(downloaded*10)/10.0 + "/" + chunkSize + "(" + Math.round((downloaded/chunkSize)*1000)/10.0 + ")"); } - // TODO: Mark the chunk as completed. - // TODO: Record the chunk end time in chunkStatus. - // TODO: Print a message that this chunk has finished downloading. + chunkStatus.setCompleted(true); + + long endTime = System.currentTimeMillis(); + chunkStatus.setEndTimeMs(endTime); + + long duration = endTime - startTime; + + System.out.println("[ Worker - " + chunkId +" ]Finished downloading chunk " + chunkId + "in" + (duration/1000.0) + "seconds"); } } diff --git a/src/main/java/Main.java b/src/main/java/Main.java index 82bd239..402b2a0 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -33,46 +33,39 @@ public class Main { Thread workerThread = new Thread(worker, "Worker-" + chunk.getChunkId()); workerThreads.add(workerThread); - - // TODO: - // Students may print helpful debug information here, - // for example which chunk is assigned to which worker thread. + System.out.println("Assigned chunk " + chunk.getChunkId() + "to thread: " + workerThread.getName()); } // 4. Create and start monitor thread ProgressMonitor monitor = new ProgressMonitor(config, chunks); Thread monitorThread = new Thread(monitor, "Progress-Monitor"); - // TODO: - // Start the monitor thread before starting the workers - // so that progress can be displayed while downloading happens. - // - // Example idea: - // monitorThread.start(); + monitorThread.start(); // 5. Start worker threads - // TODO: - // Start each worker thread in workerThreads. - // Use a loop and call start() on each thread. + for (Thread thread : workerThreads){ + thread.start(); + } // 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(); - // } + for (Thread thread : workerThreads) { + try{ + thread.join(); + }catch (InterruptedException e){ + System.out.println("Main thread interrupted while waiting for" + thread.getName());} + } - // 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. - + try{ + monitorThread.join(); + }catch (InterruptedException e){ + System.out.println("Main thread interrupted while waiting for monitor"); + } // NOTE: // this final report may show 0 progress because no worker has actually run yet. // Until students complete the thread start/join TODOs above, diff --git a/src/main/java/ProgressMonitor.java b/src/main/java/ProgressMonitor.java index 475c0e0..0a5f217 100644 --- a/src/main/java/ProgressMonitor.java +++ b/src/main/java/ProgressMonitor.java @@ -6,17 +6,21 @@ public class ProgressMonitor implements Runnable { private final int totalSizeMB; private final List chunks; private final long monitorDelayMs; + private double previousTotalDownloadedMB; + private long previousTimeMs; + public ProgressMonitor(DownloadConfig config, List chunks) { this.fileName = config.getFileName(); this.totalSizeMB = config.getTotalSizeMB(); this.chunks = chunks; this.monitorDelayMs = 500; + this.previousTotalDownloadedMB = 0.0; + this.previousTimeMs = System.currentTimeMillis(); } @Override public void run() { - // TODO: // Repeatedly check chunk progress until all chunks are completed. // In each loop: // 1. Read the downloaded size from every chunk @@ -24,9 +28,7 @@ public class ProgressMonitor implements Runnable { // 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 - - + // 6. Otherwise, sleep for monitorDelayMs and continue while (true) { double totalDownloadedMB = 0.0; int completedChunks = 0; @@ -44,19 +46,56 @@ public class ProgressMonitor implements Runnable { percent = (totalDownloadedMB * 100.0) / totalSizeMB; } + long currentTimeMs = System.currentTimeMillis(); + long timeDiffMs = currentTimeMs - previousTimeMs; + double downloadedDiff = totalDownloadedMB - previousTotalDownloadedMB; + + double speedMBps = 0.0; + if (timeDiffMs > 0){ + speedMBps = (downloadedDiff * 1000.0) / timeDiffMs; + } + + double etaSeconds = 0.0; + if (speedMBps > 0 && totalDownloadedMB < totalSizeMB){ + double remainingMB =totalSizeMB - totalDownloadedMB; + etaSeconds = remainingMB / speedMBps; + } + + previousTotalDownloadedMB = totalDownloadedMB; + previousTimeMs = currentTimeMs; + + + int barWidth = 50; + int filledWidth = (int)(barWidth * percent / 100.0); + + System.out.print("["); + for (int i = 0; i < barWidth; i++) { + if (i < filledWidth){ + System.out.print("="); + }else if (i == filledWidth && percent< 100){ + System.out.print(">"); + }else { + System.out.print(" "); + } + } + System.out.print("]"); + 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 , ETA: %s", fileName, totalDownloadedMB, (double) totalSizeMB, percent, completedChunks, - chunks.size() + chunks.size(), + speedMBps, + formatETA(etaSeconds) ); - - // TODO: - // If all chunks are completed, print a final message and exit the loop + if (completedChunks == chunks.size()){ + System.out.println("All chunks have been downloaded successfully!"); + break; + } try { Thread.sleep(monitorDelayMs); @@ -66,4 +105,20 @@ public class ProgressMonitor implements Runnable { } } } + private String formatETA(double etaSeconds){ + if (etaSeconds <= 0){ + return "Calculating ..."; + } + int hours = (int)(etaSeconds/3600); + int minutes = (int)((etaSeconds%3600)/60); + int secondes = (int)(etaSeconds %60); + + if (hours > 0){ + return String.format("%dh %dm %ds" , hours, minutes, secondes); + }else if(minutes > 0){ + return String.format("%dm %ds" , minutes, secondes); + }else{ + return String.format("%ds", secondes); + } + } } From 20f8f49c8ec0893f54673782c6665065fba58c5d Mon Sep 17 00:00:00 2001 From: ZahraSadatMirvakili Date: Wed, 3 Jun 2026 00:11:26 +0330 Subject: [PATCH 2/3] redmi + multithreading --- Report.md | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 Report.md diff --git a/Report.md b/Report.md new file mode 100644 index 0000000..3abf6e5 --- /dev/null +++ b/Report.md @@ -0,0 +1,82 @@ +# Advanced Programing - 8 Report + +## Theoretical Questions Answers + +--- + +## 1. start() vs run() +### Output: + +-Calling run() +-Running in: main +-Calling start() +-Running in: Thread-2 + +### Explanation +When calling "t1.run()" directly, the run method executes in the current thread (main). +When calling "t2.start()" , java creates a new thread and executes run() in that thread (Thread-2). + +### Difference: + +|start()|run()| +|-------|-----| +|Creates new thread|No new thread| +|Executes run() in new thread|Executed like nurmal method| +|Can only be called once|Can be called multiple times| + +--- + +## Deamon Threads + +### Output(with demon): + +-Main thread ends +-Deamon thread running... +-(Program terminates quickly) + +### Output(without demon): + +-Main thread ends. +-Deamon thread running... +-(20 times) + +### Explanation +-A daemon thread is a background thread that does NOT prevent the JVM from exiting. +When the main thread (non-daemon) finishes, +the JVM checks if there are any non-daemon threads still running. +Since only the daemon thread remains, +the JVM terminates immediately without waiting for the daemon thread to complete its 20 iterations. + +-Without setDaemon(true), the thread becomes a user thread (non-daemon) . +User threads prevent the JVM from exiting until they complete. +Therefore, the JVM waits for the thread to finish all 20 iterations before terminating the program. + +--- + +### Real-life use cases: +1. Garbage Collector (GC) +2. Auto-save features +3. Background logging +4. Session cleanup in web servers + +--- + +## 3. Lambda Expressions + +### Output: + +-Thread is running using a...! + +### What is `() -> {}`? +This is a **Lambda Expression** introduced in Java. + + +### Comparison: + +| Traditional | Lambda | +|-------------|--------| +| Needs separate class | No separate class | +| More code | Concise | +| `new Thread(new Runnable(){...})` | `new Thread(() -> {...})` | + +--- \ No newline at end of file From 71517ef506039ffd16ce92ff4c82e13293c879d6 Mon Sep 17 00:00:00 2001 From: ZahraSadatMirvakili Date: Wed, 3 Jun 2026 00:17:08 +0330 Subject: [PATCH 3/3] redmi2 + multithreading --- Report.md | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/Report.md b/Report.md index 3abf6e5..9e71b1f 100644 --- a/Report.md +++ b/Report.md @@ -7,10 +7,13 @@ ## 1. start() vs run() ### Output: --Calling run() --Running in: main --Calling start() --Running in: Thread-2 +Calling run() + +Running in: main + +Calling start() + +Running in: Thread-2 ### Explanation When calling "t1.run()" directly, the run method executes in the current thread (main). @@ -30,24 +33,28 @@ When calling "t2.start()" , java creates a new thread and executes run() in that ### Output(with demon): --Main thread ends --Deamon thread running... --(Program terminates quickly) +Main thread ends + +Deamon thread running... + +(Program terminates quickly) ### Output(without demon): --Main thread ends. --Deamon thread running... --(20 times) +Main thread ends. + +Deamon thread running... + +(20 times) ### Explanation --A daemon thread is a background thread that does NOT prevent the JVM from exiting. +A daemon thread is a background thread that does NOT prevent the JVM from exiting. When the main thread (non-daemon) finishes, the JVM checks if there are any non-daemon threads still running. Since only the daemon thread remains, the JVM terminates immediately without waiting for the daemon thread to complete its 20 iterations. --Without setDaemon(true), the thread becomes a user thread (non-daemon) . +Without setDaemon(true), the thread becomes a user thread (non-daemon) . User threads prevent the JVM from exiting until they complete. Therefore, the JVM waits for the thread to finish all 20 iterations before terminating the program. @@ -65,7 +72,7 @@ Therefore, the JVM waits for the thread to finish all 20 iterations before termi ### Output: --Thread is running using a...! +Thread is running using a...! ### What is `() -> {}`? This is a **Lambda Expression** introduced in Java.