I have a method that will be rarely called. This method collect garbage in db. I don't want to make user to wait for server response so i decided to call this method from new thread from my service layer. i'm using Spring.
Service class:
#Service
#Transactional
public class UploadService {
#Resource(name = "UploadDAO")
private UploadDao uploadDao;
Method that i don't want to wait for
public void collectBlobGarbage(){
Thread th = new Thread(new Runnable() {
#Override
public void run() {
uploadDao.collectBlobGarbage();
}
});
th.start();
}
Is it a good way to do like this?
If you have Spring on your classpath you might as well use #Async
#Async
public CompletableFuture<Void> collectBlobGarbage() throws InterruptedException {
CompletableFuture.completeFuture(uploadDao.collectBlobGarbage());
}
On your main class you need to use #EnableAsync like:
#SpringBootApplication
#EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
And you need an executor bean:
#Bean
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("Stackoverflow-");
executor.initialize();
return executor;
}
I think the provided solution can potentially cause a lot of threads on you server.
As an alternative, you can consider using Executors.newSingleThreadExecutor in such a way that you will get an executor service that is limited only to one thread—so you will never create more than one thread—and that is what you need.
Also as you are using Spring, consider configuring a SingleThreadExecutor instantiation as a separate bean—in such a way that you will be able to change the implementation of ExecutorService in the future.
Related
I'm using #EnableAsync and #Async for enabling asynchronous call when submitting request to thread pool. This work well, as it doesn't block the flow at future.get(). Now, we added another bean processExecutor within MySpringBootApplication class. But for this we wanted to wait till it completes all the task when submitted to this executor. But actually, it continues and doesn't wait at future.get(). Can someone help me, what can I make change so that it waits for all thread to complete ?
#SpringBootApplication
#EnableAsync
public class MySpringBootApplication {
private static final Logger logger = LoggerFactory.getLogger(MySpringBootApplication.class);
#Bean(name="processExecutor")
public TaskExecutor workExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setThreadNamePrefix("Async-");
threadPoolTaskExecutor.setCorePoolSize(3);
threadPoolTaskExecutor.setMaxPoolSize(3);
threadPoolTaskExecutor.setQueueCapacity(600);
threadPoolTaskExecutor.afterPropertiesSet();
logger.info("ThreadPoolTaskExecutor set");
return threadPoolTaskExecutor;
}
#Bean(name="calculateExecutor")
public TaskExecutor calExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setThreadNamePrefix("Async-");
threadPoolTaskExecutor.setCorePoolSize(3);
threadPoolTaskExecutor.setMaxPoolSize(3);
threadPoolTaskExecutor.setQueueCapacity(600);
threadPoolTaskExecutor.afterPropertiesSet();
logger.info("ThreadPoolTaskExecutor set");
return threadPoolTaskExecutor;
}
public static void main(String[] args) throws Exception {
SpringApplication.run(MySpringBootApplication.class,args);
}
}
Following is Service class
#Service
public class ProcessServiceImpl implements ProcessService {
private static final Logger logger = LoggerFactory.getLogger(ProcessServiceImpl.class);
#Async("processExecutor")
#Override
public Future<String> process() {
logger.info("Received request to process in ProcessServiceImpl.process()");
//API call which takes 2-3 seconds to complete
return new AsyncResult<String>(resp.getBody().getDataId())
}
}
I'm calling future.get() from other component class.
I created a simple spring boot application with scheduled (#Scheduled) task. In that scheduled task, I would like to call async function with #Async, but I can see it still runs on the scheduling thread without switch to another thread. I also tried to customise executor, but no luck. Here are some codes.
I also already enable async in main class
public class scheduledService {
#Scheduled(fixedRateString = "${config.scheduleInterval}")
public void pollDataWithFixSchedule() {
AsyncService service = new AsyncService();
service.asyncCall();
service.asyncCall();
service.asyncCall();
asyncCall();
}
}
public class AsyncService {
#Async()
public void asyncCall(){
System.out.printly("Current thread -- {}",Thread.currentThread().getName()))
Thread.sleep(10000);
}
}
#Bean(name = "MyThreadPoolExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(7);
executor.setMaxPoolSize(42);
executor.setQueueCapacity(11);
executor.setThreadNamePrefix("MyThreadPoolExecutor-");
executor.initialize();
return executor;
}
#SpringBootApplication
#EnableScheduling
#EnableAsync
public class ScheduledApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(ScheduledApplication.class);
application.setBannerMode(Banner.Mode.OFF);
application.run(args);
}
}
according to Baeldung:
#Async has two limitations:
it must be applied to public methods only
self-invocation – calling the async method from within the same class – won't work
The reasons are simple – the method needs to be public so that it can be proxied. And self-invocation doesn't work because it bypasses the proxy and calls the underlying method directly.
so you can put your async method in a service and use it from there
you need to autowire AsyncService, do not create new object like
AsyncService service = new AsyncService();
Also, annotate your scheduledService class with #Service or #Component
#Service
public class scheduledService {
#Autowired
private AsyncService service ;
#Scheduled(fixedRateString = "${config.scheduleInterval}")
public void pollDataWithFixSchedule() {
service.asyncCall();
service.asyncCall();
service.asyncCall();
}
}
不要在同一个类中调用异步方法
Do not call asynchronous methods in the same class.
将异步任务单独放到一个类 并且在这个类上加上#Component
Put asynchronous tasks into a single class and add # component to this class
Use #EnableAsync on the top of class where you are creating async bean, not on the ScheduledApplication.
I have a Spring Boot Application that uses CommandLineRunner and the Spring #Async annotation to run a method asynchronously. It all works fine, but when all of my threads complete, the application just hangs instead of exiting.
Here is a minimal example of what I have in my application:
Application.java:
#SpringBootApplication
#EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
ApplicationStartup.java:
#Component
public class ApplicationStartup implements CommandLineRunner {
private final AsyncService asyncService;
#Inject
public ApplicationStartup(final AsyncService asyncService) {
this.asyncService = asyncService;
}
#Override
public void run(final String... strings) throws Exception {
//my logic is more complicated than this, but this illustrates my point
for (int i = 0; i < 1000; i++) {
asyncService.runAsyncMethod();
}
}
}
AsyncService.java:
#Service
#Transactional
public class AsyncService {
#Async
public void runAsyncMethod() {
//perform call to an API and process results
}
}
ExecutorConfig.java:
#Configuration
public class ExecutorConfig() {
#Bean
public ThreadPoolTaskExecutor asyncExecutor() {
final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(64);
executor.setMaxPoolSize(64);
executor.setQueueCapacity(500);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setThreadNamePrefix("Scrub-");
executor.setKeepAliveSeconds(60);
executor.initialize();
return executor;
}
}
All of my threads make the call to runAsyncMethod() and every method call completes successfully, but then the application just hangs.
I tried changing some of the executor settings around. I didn't have the keepAliveSeconds at first, so I thought adding that would fix it, but it still hung after all threads were complete. I changed corePoolSize to 0, and that made the application exit when it was done, but it only used 1 thread the whole time.
Any ideas as to why the application is not exiting with the configuration above?
You missed to join the asynchronous jobs, that's why the run method exits (far) before all threads complete - and the awkward behavior is "more comprehensible".
According to doc, you could join like:
...
CompletableFuture<Void>[] myJobs = new CompletableFuture<>[N];
...
for (int i = 0; i < N; i++) {
myJobs[i] = asyncService.runAsyncMethod();
}
...
CompletableFuture.allOf(myJobs).join();
And your runAsyncMethod() would need to return a CompletableFuture<Void>. To do so, you can just return CompletableFuture.completedFuture(null);
Even if the marked as correct answer is valid. This is not full answer.
Without #EnableAsync and without WEB environment .web(WebApplicationType.NONE) the spring boot app automatically stop once started(as there is nothing to do/wait). So even if you don't do apringApp.close() in your app but only app.run(commandLine), the .close() method call automatically.
But once you added #EnableAsync - the behavior changes, as there might be async work, so app is not stopped once started. And if there is not stopping code, the app remain working (hangs).
For fixing this you need to do 2 things:
in the run method do wait all async work
implicitly call .close()after app started
Sample:
#EnableAutoConfiguration
#EnableAsync
public static class SpringApp extends SpringApplication {
#Bean
public TaskExecutor taskExecutor () {
return new SimpleAsyncTaskExecutor();
}
#Autowired
private Service service;
#EventListener
public void handleContextRefresh(ContextRefreshedEvent event){
CompletableFuture<Void> aggregateFuture = service.doWork();
// avoid exiting this method before all job complected prevents app from hanging
aggregateFuture.join();
}
}
public static void main(String[] args) {
SpringApplicationBuilder app = new SpringApplicationBuilder(SpringApp.class).web(WebApplicationType.NONE);
app.run()
.close(); // <--- THIS!
}
Using Spring Boot 1.5.2.RELEASE and the #Async annotation seems to be ignored.
Have setup the environment like this:
#SpringBootApplication
#EnableAsync
public class Application extends AsyncConfigurerSupport {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("async-task-");
executor.initialize();
return executor;
}
...
... the async method itself:
#Service
public class MyService {
#Async
public Future<Long> asyncTask() throws Exception {
Long test = 1023L;
Thread.sleep(10000);
return new AsyncResult<>(test);
}
}
... now I'm trying to use this:
#RestController
public MyController {
#Autowired
public MyService myService;
#PostMapping("/test")
public ResponseEntity<MyResponse> test() {
return new ResponseEntity<>(
new MyResponse(myService
.asyncTask()
.get()),
HttpStatus.OK);
}
}
... and the controller method still hangs for 10sec instead of to be immediatelly returned.
The #Async method is called from the different object. It's neither private nor transactional one as it mentioned at the similar questions.
How to let the method to be invoked asynchronously?
You should have a look at the
Future#get javadoc:
Waits if necessary for the computation to complete, and then retrieves
its result.
You are transforming your async method to a synchronous call by calling get method.
Thus instead of calling get, just return the Future. Spring MVC supports future as return type:
A ListenableFuture or CompletableFuture/CompletionStage can
be returned when the application wants to produce the value from a
thread pool submission.
Example:
return myService.asyncTask().thenApply(r -> ResponseEntity.ok(new MyResponse(r)));
Your test() function is invoking get() on the Future instance. The documentation for this function states: "Waits if necessary for the computation to complete, and then retrieves its result."
So rather than invoking get(), you likely want to return some sort if ID that the caller can use to retrieve the result at a later time (or switch to a synchronous response).
I know this question was asked couple of times but none have provided a correct answer so reposting
I have a Spring4-Jersey webservice that runs inside Tomcat 7.
I am using Spring's ThreadPoolTaskExecutor to process some messages off a queue. I have a bean that uses #Scheduled which submits tasks to the executor every 1000 millis.
However, I have noticed when I shutdown Tomcat, it warns me that it can't shutdown some tasks.
SEVERE: The web application appears to have started a thread named [taskExecutor-9] but has failed to stop it. This is very likely to create a memory leak.
org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
this what I have in code to initialize taskExecutor
#Bean(destroyMethod="shutdown")
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(100);
}
http://docs.spring.io/spring/docs/3.2.0.RC1_to_3.2.0.RC2/changes/docdiffs_org.springframework.scheduling.annotation.html
mentions that spring would take care of the threads that i created; but unfortunately it doesn't seem to be case...
Could someone provide any pointers ??
As its a web-application, you can try something like below;
Your SchedulingConfiguration Class
#Configuration
#EnableScheduling
public class SchedulerConfig implements SchedulingConfigurer {
/* Beans and Other Stuff */
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(workers());
}
#Bean(name = "executorService")
ExecutorService workers() {
return Executors.newScheduledThreadPool(100);
}
}
ShutDown The ExecutorService in ServletContextListener's contextDestroyed method.
#Configuration
public class CustomServletContextListener implements ServletContextListener {
#Autowired
private ExecutorService executorService;
#Override
public void contextInitialized(ServletContextEvent context) {
/* Do stuff If Required */
}
#Override
public void contextDestroyed(ServletContextEvent context) {
executorService.shutdown();
}
}
Worked for me and I use Tomcat8.