Consider the following scenario: You want to write a Java program that many users can interact with at the same time. When a user registers a request, the application must create a task for him, which will be done, for example, ten seconds later. But the user can cancel the task any time before it starts. The question is, what is the best way to implement this scenario?
Consider the following code snippet and optimize it if possible.
public class OurServer {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(100);
Map<User, ScheduledFuture<?>> scheduledFutures = new HashMap<>();
public static void main(String[] args) {}
public void createTaskRequest (Request req) {
ScheduledFuture<?> sf = executor.schedule(new Runnable() {
@Override
public void run() {
//doSomeTasks
scheduledFutures.remove(req);
}
}, 10, TimeUnit.SECONDS);
scheduledFutures.put(req,sf);
}
public void cancelTaskRequest (Request req) {
scheduledFutures.get(req).cancel(true);
}
}
Is this way of using Map and ScheduledFuture classes correct? any better alternatives?
You can do several improvements:
Runnable
into an inner class for better readability (you can use a record with a recent Java version).java.util.ConcurrentMap
- Java provides java.util.concurrent.ConcurrentHashMap
and java.util.concurrent.ConcurrentSkipListMap
, for your application both should be sufficient.executor.schedule
to the map - otherwise the cancellation will be lost. I used the computeIfAbsent
and computeIfPresent
to add and remove the entries to the map, those are atomic.package examples.stackoverflow.q76639234;
import java.util.Map;
import java.util.concurrent.*;
public class OurServer {
private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(100, new OurThreadFactory());
private final ConcurrentMap<Request, ScheduledFuture<?>> scheduledFutures = new ConcurrentHashMap<>();
public void createTaskRequest(Request req) {
scheduledFutures.computeIfAbsent(req, request -> {
OurTaskRunnable command = new OurTaskRunnable(request, scheduledFutures);
return executor.schedule(command, 10, TimeUnit.SECONDS);
});
}
public void cancelTaskRequest(Request req) {
scheduledFutures.computeIfPresent(req, (r, future) -> {
future.cancel(true);
return null; // this removes the value from the map
});
}
private record OurTaskRunnable(Request request, Map<Request, ?> schedulerMap) implements Runnable {
@Override
public void run() {
//doSomeTasks
schedulerMap.remove(request);
}
}
private static class OurThreadFactory implements ThreadFactory {
private final ThreadFactory delegate = Executors.defaultThreadFactory();
@Override
public Thread newThread(Runnable r) {
Thread thread = delegate.newThread(r);
thread.setDaemon(true);
return thread;
}
}
}