คำถาม
คุณสร้างตัวโหลดพื้นหลังที่เหมาะสมใน Java 8 ได้อย่างไร? เงื่อนไข:
- ควรโหลดข้อมูลในพื้นหลัง
- หลังจากการโหลดข้อมูลควรจะปรากฏขึ้น
- ในขณะที่โหลดข้อมูลไม่จำเป็นต้องยอมรับคำขอใด ๆ เพิ่มเติม
- หากมีการร้องขอในขณะที่ข้อมูลถูกโหลดอีกครั้งการโหลดควรกำหนดเวลาหลังจากหมดเวลาที่แน่นอน (เช่น 5 วินาที)
จุดประสงค์คือเพื่อรับคำขอโหลดซ้ำ แต่ไม่ใช่ฐานข้อมูลที่เต็มไปด้วยคำขอ
MCVE
นี่คือ MCVE ประกอบด้วยงานพื้นหลังซึ่งจำลองการโหลดโดยเรียกใช้ Thread.sleep เป็นเวลา 2 วินาที งานถูกกำหนดเวลาไว้ทุก ๆ วินาทีซึ่งโดยธรรมชาติจะนำไปสู่การทับซ้อนของงานโหลดพื้นหลังซึ่งควรหลีกเลี่ยง
public class LoadInBackgroundExample {
/**
* A simple background task which should perform the data loading operation. In this minimal example it simply invokes Thread.sleep
*/
public static class BackgroundTask implements Runnable {
private int id;
public BackgroundTask(int id) {
this.id = id;
}
/**
* Sleep for a given amount of time to simulate loading.
*/
@Override
public void run() {
try {
System.out.println("Start #" + id + ": " + Thread.currentThread());
long sleepTime = 2000;
Thread.sleep( sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("Finish #" + id + ": " + Thread.currentThread());
}
}
}
/**
* CompletableFuture which simulates loading and showing data.
* @param taskId Identifier of the current task
*/
public static void loadInBackground( int taskId) {
// create the loading task
BackgroundTask backgroundTask = new BackgroundTask( taskId);
// "load" the data asynchronously
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(new Supplier<String>() {
@Override
public String get() {
CompletableFuture<Void> future = CompletableFuture.runAsync(backgroundTask);
try {
future.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
return "task " + backgroundTask.id;
}
});
// display the data after they are loaded
CompletableFuture<Void> future = completableFuture.thenAccept(x -> {
System.out.println( "Background task finished:" + x);
});
}
public static void main(String[] args) {
// runnable which invokes the background loader every second
Runnable trigger = new Runnable() {
int taskId = 0;
public void run() {
loadInBackground( taskId++);
}
};
// create scheduler
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
ScheduledFuture<?> beeperHandle = scheduler.scheduleAtFixedRate(trigger, 0, 1, TimeUnit.SECONDS);
// cancel the scheudler and the application after 10 seconds
scheduler.schedule(() -> beeperHandle.cancel(true), 10, TimeUnit.SECONDS);
try {
beeperHandle.get();
} catch (Throwable th) {
}
System.out.println( "Cancelled");
System.exit(0);
}
}
ผลลัพธ์คือ:
Start #0: Thread[ForkJoinPool.commonPool-worker-2,5,main]
Start #1: Thread[ForkJoinPool.commonPool-worker-4,5,main]
Start #2: Thread[ForkJoinPool.commonPool-worker-6,5,main]
Finish #0: Thread[ForkJoinPool.commonPool-worker-2,5,main]
Background task finished:task 0
Finish #1: Thread[ForkJoinPool.commonPool-worker-4,5,main]
Background task finished:task 1
Start #3: Thread[ForkJoinPool.commonPool-worker-4,5,main]
Finish #2: Thread[ForkJoinPool.commonPool-worker-6,5,main]
Background task finished:task 2
Start #4: Thread[ForkJoinPool.commonPool-worker-6,5,main]
Start #5: Thread[ForkJoinPool.commonPool-worker-2,5,main]
Finish #3: Thread[ForkJoinPool.commonPool-worker-4,5,main]
Background task finished:task 3
Start #6: Thread[ForkJoinPool.commonPool-worker-4,5,main]
Finish #4: Thread[ForkJoinPool.commonPool-worker-6,5,main]
Background task finished:task 4
Finish #5: Thread[ForkJoinPool.commonPool-worker-2,5,main]
Background task finished:task 5
Start #7: Thread[ForkJoinPool.commonPool-worker-2,5,main]
Finish #6: Thread[ForkJoinPool.commonPool-worker-4,5,main]
Start #8: Thread[ForkJoinPool.commonPool-worker-6,5,main]
Background task finished:task 6
Start #9: Thread[ForkJoinPool.commonPool-worker-4,5,main]
Finish #7: Thread[ForkJoinPool.commonPool-worker-2,5,main]
Background task finished:task 7
Start #10: Thread[ForkJoinPool.commonPool-worker-2,5,main]
Finish #8: Thread[ForkJoinPool.commonPool-worker-6,5,main]
Background task finished:task 8
Cancelled
เป้าหมายคือต้องข้าม # 1 และ # 2 เนื่องจาก # 0 ยังทำงานอยู่
ปัญหา
คุณตั้งกลไกการบล็อกอย่างถูกต้องที่ไหน? ควรใช้การซิงโครไนซ์หรือไม่ หรือบางAtomicBoolean
? และถ้าเป็นเช่นนั้นมันควรอยู่ในget()
วิธีการหรือที่อื่น ๆ ?
ExecutorService
ขนาดเธรดพูลด้วย 1 หรือไม่?
BlockingQueue
หรือไม่