Merge pull request 'Develop' (#1) from develop into main

Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
2026-07-31 23:55:15 +00:00
4 changed files with 116 additions and 57 deletions
+57
View File
@@ -0,0 +1,57 @@
## Theoretical Questions
### 1. `start()` vs `run()`
- What output do you get from the program? Why?
```
Calling run()
Running in: main
Calling start()
Running in: Thread-2
```
→ When `t1.run()` is called directly, we're calling the `run()` method like a
normal method and no new thread is created. The `run()` method is executed on the
current thread (main); so `Thread.currentThread().getName()` returns "main".
<br/> When `t2.start()` is called: `start()` creates a new thread named "Thread-2".
The new thread automatically calls `run()` so `Thread.currentThread().getName()` returns "Thread-2".
- Whats the difference in behavior between calling `start()` and `run()`?
→ `start()`: creates a new thread and is executed on a new thread.
`run()`: method doesn't create new thread and is executed on the current thread.
### 2. Daemon Threads
- What output do you get from the program? Why?
```
Main thread ends.
Daemon thread running...
```
→ The thread is set as a Daemon thread. The main thread ends immediately after
printing "Main thread ends." and the JVM exits without waiting for the daemon thread to complete its loop.
- What happens if you remove `thread.setDaemon(true)`?
→ The thread becomes a user thread. The main thread still ends,
but the JVM will not exit because there is still a live user thread.
The program will print all 20 "Daemon thread running..." messages.
- What are some real-life use cases of daemon threads?
→ Spell Checker / Grammar Checker in Word.
### 3. A shorter way to create threads
- What output do you get from the program?
```
Thread is running using a ...!
```
- What is the `() -> { ... }` syntax called?
→ lambda expression
- How is this code different from creating a class that extends `Thread` or implements `Runnable`?
→ The code uses a lambda expression to define the `run()` method inline.
When you extend `Thread`, you must create a separate subclass, override the `run()` method,
and then instantiate that subclass. This gives you the ability to add
new fields or methods to your thread class, but it also means you cannot extend any other class.
When you implement `Runnable` using an anonymous class (the old way before lambdas), you write:
`new Runnable() { public void run() { ... } }`.
+31 -11
View File
@@ -1,3 +1,4 @@
import java.sql.SQLOutput;
import java.util.Random; import java.util.Random;
/** /**
@@ -21,23 +22,42 @@ 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.printf("worker-%d starting download of chunk (size: %.2f MB)%n", chunkStatus.getChunkId(), chunkStatus.getChunkSizeMB());
while (downloaded < chunkStatus.getChunkSizeMB()) { while (downloaded < chunkStatus.getChunkSizeMB()) {
// TODO: Generate a random sleep delay between min and max delay. int delay = config.getMinStepDelayMs() + random.nextInt(config.getMaxStepDelayMs()-config.getMinStepDelayMs()+1);
// TODO: Sleep for that delay. try
// TODO: Generate a random download amount for this step. {
// TODO: Increase downloaded, but do not go beyond chunk size. Thread.sleep(delay);
// TODO: Save the updated downloaded value into chunkStatus. } catch (InterruptedException e)
// TODO: Optionally print step-by-step progress. {
Thread.currentThread().interrupt();
System.err.printf("worker-%d interrupted%n", chunkStatus.getChunkId());
break;
} }
// TODO: Mark the chunk as completed. double stepSize = config.getMinStepDownloadMB() + random.nextDouble() * (config.getMaxStepDownloadMB() - config.getMinStepDownloadMB());
// TODO: Record the chunk end time in chunkStatus. downloaded += stepSize;
// TODO: Print a message that this chunk has finished downloading. if (downloaded > chunkStatus.getChunkSizeMB())
{
downloaded = chunkStatus.getChunkSizeMB();
} }
chunkStatus.setDownloadedMB(downloaded);
System.out.printf("worker-%d progress: %.2f / %.2f MB (%.1f%%)%n",
chunkStatus.getChunkId(),
downloaded,
chunkStatus.getChunkSizeMB(),
(downloaded / chunkStatus.getChunkSizeMB()) * 100);
}
chunkStatus.setCompleted(true);
chunkStatus.setEndTimeMs(System.currentTimeMillis());
System.out.printf("worker-%d chunk download completed. Time: %d ms%n",
chunkStatus.getChunkId(),
chunkStatus.getDownloadDurationMs()); }
} }
+22 -33
View File
@@ -34,48 +34,37 @@ public class Main {
workerThreads.add(workerThread); workerThreads.add(workerThread);
// TODO: System.out.println("Created worker thread: " + workerThread.getName() +
// Students may print helpful debug information here, " for chunk #" + chunk.getChunkId() +
// for example which chunk is assigned to which worker thread. " (size: " + chunk.getChunkSizeMB() + " MB)");
} }
// 4. Create and start monitor 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");
// TODO: monitorThread.start();
// Start the monitor thread before starting the workers System.out.println("monitor thread started.");
// so that progress can be displayed while downloading happens.
//
// Example idea:
// monitorThread.start();
// 5. Start worker threads for (Thread thread : workerThreads)
// TODO: {
// Start each worker thread in workerThreads. thread.start();
// Use a loop and call start() on each thread. System.out.println("started: "+thread.getName());
}
// 6. Wait for workers to finish try
// TODO: {
// Wait for all worker threads to complete by calling join(). for (Thread thread : workerThreads)
// This should be done inside a try-catch block for InterruptedException. {
// thread.join();
// Hint:
// for (Thread thread : workerThreads) {
// thread.join();
// }
// TODO: }
// After all workers finish, the monitor thread may also need to stop. } catch (InterruptedException e)
// Depending on how ProgressMonitor is implemented, students may: {
// - wait for it to finish on its own, or System.out.println("main thread interrupted while waiting for workers.");
// - add a stopping mechanism in ProgressMonitor later. Thread.currentThread().interrupt();
// return;
// 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 // 7. Print final report
System.out.println(); System.out.println();
+5 -12
View File
@@ -16,16 +16,6 @@ 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;
@@ -55,8 +45,11 @@ public class ProgressMonitor implements Runnable {
); );
// TODO: if (completedChunks == chunks.size())
// If all chunks are completed, print a final message and exit the loop {
System.out.printf("All chunks completed for %s. Monitor stopping>%n", fileName);
break;
}
try { try {
Thread.sleep(monitorDelayMs); Thread.sleep(monitorDelayMs);