4 Commits
Author SHA1 Message Date
ShayanEdalatjoo 40d53cbb66 implementing main.java 2026-07-17 12:29:31 +03:30
ShayanEdalatjoo a3d55ca32a finishing ProgressMonitor.java 2026-07-17 11:39:11 +03:30
ShayanEdalatjoo db90455220 implement DownloadWorker.java 2026-07-17 11:22:09 +03:30
ShayanEdalatjoo 9080de66a5 adding Report.md 2026-07-17 11:10:53 +03:30
4 changed files with 90 additions and 36 deletions
+41
View File
@@ -0,0 +1,41 @@
1:
the output is Calling run()
Running in: main
Calling start()
Running in: Thread-2
t1.run() does not start a new thread.
It just calls the run() method like a normal method call, and it executes on the current thread.
t2.start() creates a new thread and then that new thread executes run(), so current Thread Name becomes Thread-2.
run() is just a normal method call, so it runs in the current thread.
start() creates a new thread, and that new thread executes run().
2:
Output:
Main thread ends.
It may also print:
Daemon thread running…
a few times, or sometimes not at all.
Why:
Because the created thread is a daemon thread. Daemon threads run in the background, and when the main thread finishes, the JVM can stop immediately.
So the daemon thread may not complete its loop.
If thread.setDaemon(true) is removed:
Then the thread becomes a user thread (non-daemon thread).
and the JVM will wait for it to finish, so "Daemon thread running..." will print many times until the loops end.
Real-life uses of daemon threads:
Garbage collection
Background auto-save
Cache cleanup
Monitoring or logging services
Timer or scheduler tasks running in background
3:
What output do you get from the program?
Thread is running using a …!
What is the () -> { ... } syntax called?
This is called a lambda expression.
How is this code different from creating a class that extends Thread or implements Runnable?
This is a shorter and cleaner way to write thread code.
Instead of creating a separate class, the task is written directly inside the lambda expression.
+25 -12
View File
@@ -21,23 +21,36 @@ 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("Chunk " + chunkStatus.getChunkId() + " started downloading.");
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()
// TODO: Generate a random download amount for this step. + random.nextInt(config.getMaxStepDelayMs() - config.getMinStepDelayMs() + 1);
// TODO: Increase downloaded, but do not go beyond chunk size. Thread.sleep(delay);
// TODO: Save the updated downloaded value into chunkStatus.
// TODO: Optionally print step-by-step progress.
}
// TODO: Mark the chunk as completed. double stepDownload = config.getMinStepDownloadMB()
// TODO: Record the chunk end time in chunkStatus. + random.nextDouble() * (config.getMaxStepDownloadMB() - config.getMinStepDownloadMB());
// TODO: Print a message that this chunk has finished downloading.
downloaded += stepDownload;
if (downloaded > chunkStatus.getChunkSizeMB()) {
downloaded = chunkStatus.getChunkSizeMB();
}
chunkStatus.setDownloadedMB(downloaded);
} catch (InterruptedException e) {
System.err.println("[Worker-" + chunkStatus.getChunkId() + "] Download interrupted.");
Thread.currentThread().interrupt();
return;
}
}
chunkStatus.setCompleted(true);
chunkStatus.setEndTimeMs(System.currentTimeMillis());
System.out.println("[Worker-" + chunkStatus.getChunkId() + "] Download completed.");
} }
} }
+20 -22
View File
@@ -34,36 +34,34 @@ public class Main {
workerThreads.add(workerThread); workerThreads.add(workerThread);
// TODO: System.out.println("Assigned Chunk " + chunk.getChunkId());
// Students may print helpful debug information here,
// for example which chunk is assigned to which worker thread.
} }
// 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");
monitorThread.start();
// 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 // 5. Start worker threads
// TODO: for (Thread thread : workerThreads)
// Start each worker thread in workerThreads. thread.start();
// Use a loop and call start() on each thread.
try {
for (Thread thread : workerThreads) {
thread.join();
}
} catch (InterruptedException e) {
System.out.println("Interrupted.");
return;
}
try {
monitorThread.join();
} catch (InterruptedException e) {
System.out.println("Interrupted.");
return;
}
// 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: // 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.
+4 -2
View File
@@ -55,8 +55,10 @@ 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.println("All chunks completed.");
break;
}
try { try {
Thread.sleep(monitorDelayMs); Thread.sleep(monitorDelayMs);