gpt4 book ai didi

java - 如何在 Java 8 中创建阻塞后台加载程序?

转载 作者:行者123 更新时间:2023-12-03 03:35:43 24 4
gpt4 key购买 nike

问题

如何在 Java 8 中创建合适的后台加载程序?条件:

  • 数据应在后台加载
  • 加载后应显示数据
  • 加载数据后,不应接受进一步的请求
  • 如果在加载数据时有请求,则应在一定的超时(例如 5 秒)后安排另一次加载

目的是 e. G。已接受重新加载请求,但数据库未充满请求。

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

目标是拥有 e。 G。 #1 和 #2 已跳过,因为 #0 仍在运行。

问题

你在哪里正确设置了阻止机制?是否应该使用同步?或者一些AtomicBoolean?如果是这样,它应该在 get() 方法内部还是其他地方?

最佳答案

您已经有一个线程池来执行任务。在另一个异步执行器中运行任务并不一定会让事情变得复杂(当您使用CompletableFuture时,ForkJoinPool)

让事情变得简单:

public static void loadInBackground(int taskId) {
// create the loading task
BackgroundTask backgroundTask = new BackgroundTask(taskId);
// No need to run in async, as it already in executor
backgroundTask.run();
}

当您使用scheduleAtFixedRate调用它时,ScheduledExecutorService将确保一次只运行一个任务。

Creates and executes a periodic action that becomes enabled first after the given initial delay, and subsequently with the given period; that is executions will commence after initialDelay then initialDelay+period, then initialDelay + 2 * period, and so on. If any execution of the task encounters an exception, subsequent executions are suppressed. Otherwise, the task will only terminate via cancellation or termination of the executor. If any execution of this task takes longer than its period, then subsequent executions may start late, but will not concurrently execute.

关于java - 如何在 Java 8 中创建阻塞后台加载程序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58796231/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com