I implemented the Spring-TaskExecutor (which is the equivalent of the JDK 1.5's Executor.) to process notifications notifications receiving from external systems.
Interface with only one method:
public interface AsynchronousService {
void executeAsynchronously(Runnable task);
}
and the corresponding implementation:
public class AsynchronousServiceImpl implements AsynchronousService {
private Executor taskExecutor;
@Override
public void executeAsynchronously(Runnable task) {
taskExecutor.execute(task);
}
@Required
public void setTaskExecutor(Executor taskExecutor) {
this.taskExecutor = taskExecutor;
}
}
Xml-configuration of the task executor (legacy application):
<bean id="taskExecutor" class="org.example.impl.NotificationPool">
<property name="corePoolSize" value="1"/>
<property name="maxPoolSize" value="1"/>
<property name="queueCapacity" value="100"/>
<property name="WaitForTasksToCompleteOnShutdown" value="true"/>
</bean>
1 is set for both, corePoolSize and maxPoolSize, since I want the tasks to execute sequentially (only 1 thread is created by the pool which process the tasks).
I want to order my task based on the date when i received the notification, therefore I need to override this function to allow priority ordering:
public class NotificationPool extends ThreadPoolExecutorFactoryBean{
@Override
protected BlockingQueue<Runnable> createQueue(int queueCapacity) {
return new PriorityBlockingQueue<>(queueCapacity);
}
}
The ThreadPoolExecutorFactoryBean exposes the ´createExecutor´ method, which I did override to create our own Executor:
@Override
protected ThreadPoolExecutor createExecutor(int corePoolSize, int maxPoolSize, int keepAliveSeconds,
BlockingQueue<Runnable> queue, ThreadFactory threadFactory,
RejectedExecutionHandler rejectedExecutionHandler) {
return new YourCustomThreadPoolExecutor (corePoolSize, maxPoolSize, keepAliveSeconds, TimeUnit.SECONDS, queue,
threadFactory, rejectedExecutionHandler);
Now, in my custom thread pool executor I can override the default afterExecute callback:
public class YourCustomThreadPoolExecutor extends ThreadPoolExecutor {
public YourCustomThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
// Here do something with your exception
}
}
Here is the Notification task class:
public class NotificationTask implements Runnable, Comparable<NotificationTask> {
private final NotificationService notificationService;
private final Notification notification;
public NotificationService(NotificationService notificationService,
Notification notification) {
this.notificationService = notificationService;
this.notification = notification;
}
@Override
public int compareTo(NotificationTask task) {
return notification.getTimestamp().compareTo(task.getTimestamp());
}
@Override
public void run() {
notificationService.processNotification(notification);
}
}
And this is how I execute it:
asynchronousService.executeAsynchronously(new NotificationTask (notificationService, notification));
I don't want only to have the queue in the memory but also persist the tasks in the database, so I have them even if the application crashes.
My first solution which come to my mind is to override the default beforeExecute
callback in the YourCustomThreadPoolExecutor
class:
public class YourCustomThreadPoolExecutor extends ThreadPoolExecutor {
public YourCustomThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
}
@Override
protected void beforeExecute(Thread t, Runnable r) {
// 1) serialize runnable object
// 2) write in the database (with a flag if it is executed true/false)
super.beforeExecute(t, r);
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
}
}
The idea is now, when I execute the task, I will check the database first, if it exists a task which is not executed (by checking a boolean flag for example). If yes, then I get this task first and execute it and update the flag. Then I continue executing the other task. So in this case, if the application crashes after executing a task, then I still make sure to execute this task after my application runs again, it doesn't get lost. What do you think of this solution?
First I tried to find a hook-method which is called before putting the tasks in the queue, so I can do the presistence of the tasks there. (Because I would prefer peristing the tasks before putting them in the queue) But I didn't find any way to do it, that's why my only chance is to override the beforeExecutue
method. This is of course not so ideal, since I only start persisting the tasks when I dequeu them, so if the application crashes all my tasks in the queue are lost.
来源:https://stackoverflow.com/questions/51022412/java-executorservice-persist-queue