For my design I need a controllable schedular. Spring boot offers an @Scheduled
annotation but that is more simplified and I do not have granular control.
So I wanted to implement my own scheduler manually.
This is the class I created:
@Slf4j
@Service
public class JobExecutor {
private static ScheduledExecutorService jobExecutor;
private static Environment env;
@Autowired
private JobExecutor(Environment env) {
JobExecutor.env = env;
}
public static ScheduledExecutorService INSTANCE() {
if (null == jobExecutor) {
synchronized (JobExecutor.class) {
if (null == jobExecutor) {
jobExecutor = Executors.newScheduledThreadPool(
Integer.parseInt(Objects.requireNonNull(env.getProperty("scheduler.jobs"))));
}
}
}
return jobExecutor;
}
}
With this approach I could simply call the static method to get a single instance.
Is this correct approach for a schedular?
I need to start and stop and shutdown the jobs. I tried guava AbstractScheduledService
but that does not seem to be working.
CodePudding user response:
This is not the correct approach for creating a singleton, because double checked locking is broken. You're using Spring, so a) your JobExecutor will be a singleton anyway, and b) will only be created if it is needed. You might as well, therefore, create your executor instance in the constructor and get rid of those static methods.
Even better, you could create schedulers as named beans, and then inject them into classes where you want them:
@Configuration
public class ExecutorConfiguration {
@Bean
public ScheduledExecutorService jobExecutor(@Value("${scheduler.jobs}") jobs) {
return Executors.newScheduledThreadPool(jobs);
}
}
This says that whenever another component needs a ScheduledExecutorService
, Spring should call this jobExecutor()
method; Spring will automatically populate the jobs
parameter from the scheduler.jobs
property because of the @Value
.
You can then inject your executor wherever you need it, for example with constructor injection (handily you're already using Lombok, so the amount of boilerplate is minimised):
@Service
@AllArgsConstructor
public class MyThingThatNeedsAScheduler {
private final ScheduledExecutorService jobExecutor;
// methods here...
}
You can also use setter or member injection, if you want.