From 1b7327b9d983ea4c55c0444a03165b71c7673878 Mon Sep 17 00:00:00 2001 From: neda Date: Sun, 19 Jul 2026 06:12:05 +0330 Subject: [PATCH] basic --- .idea/misc.xml | 2 +- Report.md | 142 +++++++++++++++++++++++++++++ src/main/java/DownloadWorker.java | 81 ++++++++++++---- src/main/java/Main.java | 59 +++++------- src/main/java/ProgressMonitor.java | 21 ++--- 5 files changed, 239 insertions(+), 66 deletions(-) create mode 100644 Report.md diff --git a/.idea/misc.xml b/.idea/misc.xml index fdc35ea..389a2fc 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,7 +8,7 @@ - + \ No newline at end of file diff --git a/Report.md b/Report.md new file mode 100644 index 0000000..9a01c6b --- /dev/null +++ b/Report.md @@ -0,0 +1,142 @@ + +# `start()` vs `run()` + +## Output + +The output will be similar to: + +```text +Calling run() +Running in: main +Calling start() +Running in: Thread-2 +``` + +(The exact order of the last two lines may vary slightly because `start()` creates a new thread that runs independently.) + +## Why? + +When `t1.run()` is called, the `run()` method is executed like a normal method call. It does not create a new thread. Since `main()` is the thread currently executing the code, `Thread.currentThread().getName()` returns `main`. + +When `t2.start()` is called, Java creates a new thread and the JVM schedules that thread to execute the `run()` method. Because the new thread was created with the name `"Thread-2"`, the output shows `Thread-2` as the executing thread. + +## Difference between `start()` and `run()` + +The main difference is that `start()` creates a new thread of execution, while `run()` only executes the method in the current thread. + +* Calling `run()` directly does not start a new thread. The code runs sequentially in the same thread that called it. +* Calling `start()` creates a new thread and then internally calls the `run()` method on that new thread. +* `start()` allows multiple threads to run concurrently, while `run()` behaves like a normal method call. + +In this example, `t1.run()` runs inside the `main` thread, but `t2.start()` runs inside a separate thread named `Thread-2`. + +## 2.Daemon Threads + +## Output + +The output will usually be: + +```text +Main thread ends. +``` + +Sometimes it may also print one or more lines like: + +```text +Daemon thread running... +Main thread ends. +``` + +The exact output depends on the timing of the JVM shutting down. + +## Why? + +The thread is marked as a daemon thread using: + +```java +thread.setDaemon(true); +``` + +Daemon threads run in the background and do not prevent the JVM from exiting. When the `main` thread finishes, there are no remaining non-daemon threads, so the JVM terminates. As a result, the daemon thread may be stopped before it completes its loop of printing messages 20 times. + +## What happens if `thread.setDaemon(true)` is removed? + +If `setDaemon(true)` is removed, the thread becomes a normal (user) thread. The JVM will wait for this thread to finish before shutting down. + +The output will look something like: + +```text +Main thread ends. +Daemon thread running... +Daemon thread running... +Daemon thread running... +... +``` + +The daemon thread will continue running until the loop completes, even though the `main` thread has already finished. + +## Real-life use cases of daemon threads + +Daemon threads are useful for background tasks that should automatically stop when the main application ends. Some examples include: + +* **Garbage collection:** The JVM uses background daemon threads to manage memory cleanup. +* **Background monitoring:** Applications can use daemon threads to monitor system resources, logs, or application status. +* **Auto-save features:** A text editor or IDE might use a daemon thread to periodically save temporary data. +* **Cache cleanup:** A server application might run a daemon thread to remove expired cache entries. +* **Scheduled background tasks:** Tasks like checking for updates or refreshing data can run as daemon threads. + +Daemon threads are mainly used for tasks that support the main application but are not essential for the application to finish running. + +# 3. A Shorter Way to Create Threads + +## Output + +The output will be: + +```text id="q7k4m3" +Thread is running using a ...! +``` + +The message is printed from the new thread created by calling `thread.start()`. + +## What is the `() -> { ... }` syntax called? + +The `() -> { ... }` syntax is called a **lambda expression** in Java. + +A lambda expression is a shorter way to write an implementation of a functional interface. In this example, it replaces the need to create a separate class that implements `Runnable`. + +The code: + +```java id="9xw2aq" +() -> { + System.out.println("Thread is running using a ...!"); +} +``` + +acts as the implementation of the `Runnable` interface's `run()` method. + +## How is this different from creating a class that extends `Thread` or implements `Runnable`? + +Using a lambda expression makes the code shorter and easier to read because it avoids creating an extra class. + +With `implements Runnable`, we normally create a separate class: + +```java id="6g5v1p" +class MyRunnable implements Runnable { + public void run() { + System.out.println("Thread running"); + } +} +``` + +With a lambda expression, the same idea can be written directly: + +```java id="v4c2km" +Thread thread = new Thread(() -> { + System.out.println("Thread running"); +}); +``` + +Extending `Thread` means creating a new class that inherits from the `Thread` class and overrides the `run()` method. This gives more control over the thread object but is less flexible because Java only allows a class to extend one class. + +Using `Runnable` or a lambda expression is generally preferred because it separates the task being performed from the thread itself and allows the code to be more reusable. diff --git a/src/main/java/DownloadWorker.java b/src/main/java/DownloadWorker.java index 546de2d..adc75be 100644 --- a/src/main/java/DownloadWorker.java +++ b/src/main/java/DownloadWorker.java @@ -1,12 +1,6 @@ import java.util.Random; -/** - * Simulates downloading a single chunk of a file. - * - *

This class is intentionally provided as a skeleton for students. - * The main multithreading and simulation logic should be completed - * in the run() method.

- */ + public class DownloadWorker implements Runnable { private final ChunkStatus chunkStatus; @@ -21,23 +15,74 @@ public class DownloadWorker implements Runnable { @Override public void run() { - // TODO: Record the chunk start time in chunkStatus. + + chunkStatus.setStartTimeMs(System.currentTimeMillis()); double downloaded = 0.0; - // TODO: Print a message that this chunk has started downloading. + + System.out.println(Thread.currentThread().getName() + + " started downloading Chunk #" + chunkStatus.getChunkId() + + " (" + chunkStatus.getChunkSizeMB() + " MB)"); 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 delay = randomBetween(config.getMinStepDelayMs(), config.getMaxStepDelayMs()); + + + try { + Thread.sleep(delay); + } catch (InterruptedException e) { + System.out.println(Thread.currentThread().getName() + " was interrupted."); + Thread.currentThread().interrupt(); + return; + } + + + double step = randomBetween(config.getMinStepDownloadMB(), config.getMaxStepDownloadMB()); + + + downloaded += step; + if (downloaded > chunkStatus.getChunkSizeMB()) { + downloaded = chunkStatus.getChunkSizeMB(); + } + + + chunkStatus.setDownloadedMB(downloaded); + + + System.out.printf("%s -> Chunk #%d: %.1f/%.1f MB (%.1f%%)%n", + Thread.currentThread().getName(), + chunkStatus.getChunkId(), + downloaded, + chunkStatus.getChunkSizeMB(), + chunkStatus.getProgressPercentage()); } - // 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); + + + chunkStatus.setEndTimeMs(System.currentTimeMillis()); + + + System.out.println(Thread.currentThread().getName() + + " finished Chunk #" + chunkStatus.getChunkId() + + " in " + chunkStatus.getDownloadDurationMs() + " ms"); } + + private int randomBetween(int min, int max) { + if (min >= max) { + return min; + } + return min + random.nextInt(max - min + 1); + } + + + private double randomBetween(double min, double max) { + if (min >= max) { + return min; + } + return min + random.nextDouble() * (max - min); + } } diff --git a/src/main/java/Main.java b/src/main/java/Main.java index 82bd239..ff31229 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -34,50 +34,41 @@ public class Main { 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() + + " (" + chunk.getChunkSizeMB() + " MB) to " + 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(); - // 5. Start worker threads - // TODO: - // Start each worker thread in workerThreads. - // Use a loop and call start() on each thread. + monitorThread.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(); - // } - // 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. + for (Thread thread : workerThreads) { + thread.start(); + } + + + for (Thread thread : workerThreads) { + try { + thread.join(); + } catch (InterruptedException e) { + System.out.println("Main thread interrupted while waiting for " + thread.getName()); + Thread.currentThread().interrupt(); + } + } + + + try { + monitorThread.join(); + } catch (InterruptedException e) { + System.out.println("Main thread interrupted while waiting for monitor."); + Thread.currentThread().interrupt(); + } - // 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("=== Final Report ==="); diff --git a/src/main/java/ProgressMonitor.java b/src/main/java/ProgressMonitor.java index 475c0e0..1a655e9 100644 --- a/src/main/java/ProgressMonitor.java +++ b/src/main/java/ProgressMonitor.java @@ -1,5 +1,6 @@ import java.util.List; + public class ProgressMonitor implements Runnable { private final String fileName; @@ -16,21 +17,11 @@ public class ProgressMonitor implements Runnable { @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 - // 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) { double totalDownloadedMB = 0.0; int completedChunks = 0; + for (ChunkStatus chunk : chunks) { totalDownloadedMB += chunk.getDownloadedMB(); @@ -44,6 +35,7 @@ public class ProgressMonitor implements Runnable { percent = (totalDownloadedMB * 100.0) / totalSizeMB; } + System.out.printf( "Progress for %s: %.1f/%.1f MB (%.2f%%), completed chunks: %d/%d%n", fileName, @@ -55,8 +47,11 @@ public class ProgressMonitor implements Runnable { ); - // TODO: - // If all chunks are completed, print a final message and exit the loop + if (completedChunks == chunks.size()) { + System.out.println("Progress monitor: all chunks completed. Stopping monitor."); + return; + } + try { Thread.sleep(monitorDelayMs); -- 2.54.0