This commit is contained in:
2026-06-27 15:34:07 +03:30
parent 9e5c715088
commit de653a85da
4 changed files with 69 additions and 75 deletions
+20
View File
@@ -0,0 +1,20 @@
خروجی برنامه به این شکل است: اول خط Calling run چاپ می‌شود، بعد خط Running in: main، بعد خط Calling start و در نهایت خط Running in: Thread-2.
دلیلش این است که وقتی ران را صدا می‌زنیم، اصلاً ترد جدیدی ساخته نمی‌شود. متد ران دقیقاً مثل یک متد معمولی در همان ترد فعلی که اینجا همان مین است اجرا می‌شود. اما وقتی استارت را می‌زنیم، جاوا یک ترد جدید در پس‌زمینه به اسم Thread-2 می‌سازد و متد ران را می‌اندازد آنجا تا اجرا شود.
فرق اصلی این دو در این است که وقتی ران را صدا می‌زنیم، کار کاملاً همگام پیش می‌رود. یعنی برنامه همانجا می‌ایستد تا اجرای متد تمام شود و بعد می‌رود خط بعدی. اما وقتی استارت را صدا می‌زنیم، کار ناهمگام می‌شود. یک ترد جدید ساخته می‌شود و کار را می‌سپارد به آن، برنامه هم دیگر معطل نمی‌ماند و سریع می‌رود سراغ اجرای خط بعدی.
خروجی برنامه احتمالاً فقط یک خط است که می‌نویسد مین ترد اندز.
دلیلش این است که در جاوا ما کلاً دو مدل ترد داریم یکی تردهای معمولی و یکی تردهای دیمون. ماشین مجازی جاوا اصلاً منتظر تمام شدن کار تردهای دیمون نمی‌ماند. یعنی به محض اینکه کار تردهای اصلی تمام شود، برنامه را می‌بندد و تردهای دیمون در هر وضعیتی که باشند درجا قطع می‌شوند.
اگر خط مربوط به تنظیم دیمون را پاک کنیم، ترد ما تبدیل می‌شود به یک ترد معمولی. آن وقت ماشین مجازی مجبور است صبر کند تا حلقه به طور کامل تمام شود و تازه بعدش برنامه بسته می‌شود.
این تردها در دنیای واقعی بیشتر برای کارهای پس‌زمینه استفاده می‌شوند که خیلی حیاتی نیستند ولی باید باشند. مثلاً سیستم تمیزکننده حافظه جاوا که خودش در پس‌زمینه حافظه را پاک می‌کند، یا قابلیت ذخیره خودکار در برنامه‌ها که هر چند دقیقه یک بار خودکار تغییرات را ذخیره می‌کنند.
خروجی برنامه به این صورت است که پیام اجرای ترد با استفاده از لامبدا چاپ می‌شود.
به این مدل کد زدن عبارت لامبدا می‌گویند که از نسخه هشت جاوا به بعد اضافه شد.
فرق این روش با ساختن کلاسی که از کلاس ترد ارث‌بری می‌کند در این است که خیلی خلاصه‌تر و جمع‌وجورتر است. دیگر نیازی نیست کلی کد اضافه بنویسیم یا یک کلاس جدا بسازیم و متد ران را بازنویسی کنیم. منطق کد را خیلی راحت همانجا می‌نویسیم. اینطوری کد خیلی تمیزتر و خواناتر می‌شود، مخصوصاً برای وقت‌هایی که فقط می‌خواهیم یک تکه کد کوچک را سریع در یک ترد اجرا کنیم.
+26 -20
View File
@@ -1,12 +1,5 @@
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 {
private final ChunkStatus chunkStatus;
@@ -21,23 +14,36 @@ public class DownloadWorker implements Runnable {
@Override
public void run() {
// TODO: Record the chunk start time in chunkStatus.
chunkStatus.setStartTimeMs(System.currentTimeMillis());
System.out.println(Thread.currentThread().getName() + " started downloading.");
double downloaded = 0.0;
double chunkSize = chunkStatus.getChunkSizeMB();
// TODO: Print a message that this chunk has started downloading.
while (downloaded < chunkSize) {
int delayMs = config.getMinStepDelayMs() + random.nextInt(config.getMaxStepDelayMs() - config.getMinStepDelayMs() + 1);
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.
try {
Thread.sleep(delayMs);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + " was interrupted.");
return;
}
double stepDownload = config.getMinStepDownloadMB() + (config.getMaxStepDownloadMB() - config.getMinStepDownloadMB()) * random.nextDouble();
downloaded += stepDownload;
if (downloaded > chunkSize) {
downloaded = chunkSize;
}
chunkStatus.setDownloadedMB(downloaded);
}
// 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 downloading.");
}
}
}
+18 -40
View File
@@ -5,7 +5,6 @@ public class Main {
public static void main(String[] args) {
System.out.println("=== Simulated Download Manager ===");
// 1. Read config
DownloadConfig config;
try {
config = ConfigReader.readConfig("download_config.txt");
@@ -19,13 +18,11 @@ public class Main {
System.out.println("Chunk count: " + config.getChunkCount());
System.out.println();
// 2. Create chunks
List<ChunkStatus> chunks = ChunkUtils.createChunks(
config.getTotalSizeMB(),
config.getChunkCount()
);
// 3. Create worker threads
List<Thread> workerThreads = new ArrayList<>();
for (ChunkStatus chunk : chunks) {
@@ -33,51 +30,32 @@ 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 " + 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 progress 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,
// 7. Print final report
System.out.println();
System.out.println("=== Final Report ===");
@@ -104,4 +82,4 @@ public class Main {
System.out.println("Downloaded total: " + downloadedMB + "/" + config.getTotalSizeMB() + " MB");
System.out.println("Simulation finished.");
}
}
}
+5 -15
View File
@@ -16,17 +16,6 @@ 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;
@@ -54,9 +43,10 @@ public class ProgressMonitor implements Runnable {
chunks.size()
);
// 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.");
break;
}
try {
Thread.sleep(monitorDelayMs);
@@ -66,4 +56,4 @@ public class ProgressMonitor implements Runnable {
}
}
}
}
}