How to best implement a multi-thread approach in Spring Boot - java

I have been struggling with implementing a multi-threaded approach to the application I am working on.
The part I want to run in parallel threads was originally constructed with a for loop going about a list.
#Service
public ApplicationServiceImpl implements ApplicationService {
#Override
public ResponseEntity<Void> startProcess(List<MyObject> myObjectList) throws Exception {
for (MyObject myObject : myObjectList) {
AnotherTypeOfObject anotherTypeOfObject = runMethodA(myObject);
YetAnotherTypeOfObject yetAnotherTypeOfObject = runMethodB(anotherTypeOfObject);
runMethodC(yetAnotherTypeOfObject, aStringValue, anotherStringValue);
runMethodD(yetAnotherTypeOfObject);
}
}
}
The methods private AnotherTypeOfObject runMethodA(MyObject myObject) {...}, private YetAnotherTypeOfObject yetAnotherTypeOfObject(AnotherTypeOfObject anotherTypeOfObject) {...}, private void runMethodC(YetAnotherTypeOfObject yetAnotherTypeOfObject, String aStringValue, String anotherStringValue) {...} and private void runMethodD(MyObject myObject) {...} only use local variables.
I have looked quite a bit to get a solution that would allow firing the threads of a list of 100s of MyObject instead of one after the other.
What I have done is create a:
#Configuration
#EnableAsync
public class AsyncConfiguration() {
#Bean(name = "threadPoolTaskExecutor")
public Executor aSyncExecutor() {
final ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(4);
threadPoolTaskExecutor.setMaxPoolSize(4);
threadPoolTaskExecutor.setQueueCapacity(50);
threadPoolTaskExecutor.setThreadNamePrefix("threadNamePrefix");
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
}
I do have loads of log.info("some recognizable text") through the methods A, B, C and D so I can make sure what is going on and I aggregated these methods into one like
private void runThreads(MyObject myObject, String aStringValue, String anotherStringValue) {
AnotherTypeOfObject anotherTypeOfObject = runMethodA(myObject);
YetAnotherTypeOfObject yetAnotherTypeOfObject = runMethodB(anotherTypeOfObject);
runMethodC(yetAnotherTypeOfObject, aStringValue, anotherStringValue);
runMethodD(yetAnotherTypeOfObject);
}
And I have tried to run the main method as:
#Override
#Async("threadPoolTaskExecutor")
public ResponseEntity<Void> startProcess(List<MyObject> myObjectList) throws Exception {
String aStringValue = myObject.getAStringValue();
String anotherStringValue = myObject.getAnotherStringValue();
myObjectList.forEach(myObject -> runThreads(myObject, aStringValue, anotherStringValue));
}
I still don't get the intended result of firing a few threads for the runThreads(...) {} method, so the processing is done in parallel.
What am I missing here?

Actually you are not parallelising the for loop, but the method that executes the for loop. A single thread in this case would execute all the loop.
You need to put the #Async on top of runThreads()
Although It's not recommended to create the executor with static configurations. Try to use the completablefuture API :
https://www.baeldung.com/java-completablefuture

If it's only for running all elements of a collection in parallel, then you can use Stream.parallel(). It uses a default ForkJoinPool with a thread per CPU core. This is the simplest method introduced in Java 8.
myObjectList.stream()
.parallel()
.forEach(myObject -> runThreads(myObject, myObject.getAStringValue(), myObject.getAnotherStringValue()));
For this you don't need any #Async or Spring-provided Executor.
You can use a custom ForkJoinPool to customize the number of threads, but the default might work well, too.
ForkJoinPool customThreadPool = new ForkJoinPool(4);
customThreadPool.invoke(
() -> myObjectList.stream()
.parallel()
.forEach(myObject -> runThreads(myObject, myObject.getAStringValue(), myObject.getAnotherStringValue())));

Related

Avoiding using Thread.sleep() when working with SqsListener

I have SQS listener, and under a certain condition, I need to wait 120 seconds before executing method1(). But as I know SQS listener works multithreaded. Is there a way to avoid using Thread.sleep() to reuse the thread rather than leave it waiting?
Here is listener code:
#SqsListener(value = "${test}", deletionPolicy = SqsMessageDeletionPolicy.ON_SUCCESS)
public void listenTransactionEvent(#Payload SQSEvent sqsEvent) {
String sqsPlainMessage = sqsEvent.getMessage();
if (sqsPlainMessage.equals("test")) {
Thread.sleep(120 * 1000L);
method1();
} else {
method2();
}
}
and configuration:
#Bean
public QueueMessageHandlerFactory queueMessageHandlerFactory(
final ObjectMapper mapper,
final AmazonSQSAsync amazonSQSAsync
) {
final QueueMessageHandlerFactory queueHandlerFactory = new QueueMessageHandlerFactory();
queueHandlerFactory.setAmazonSqs(amazonSQSAsync);
queueHandlerFactory.setArgumentResolvers(Collections.singletonList(
new PayloadMethodArgumentResolver(jackson2MessageConverter(mapper))
));
return queueHandlerFactory;
}
I believe you may be looking for java.util.Timer to schedule a TimeTask or even using a java.util.concurrent.Future could release the thread calling listenTransactionEvent, but the waiting will just change place, sometimes depending on context Thread.sleep might be an acceptable solution.
Best regards.

how to create thread on Map<k,v> that work all the time and remove key from the map?

I'm writing REST API of coupons system,
and I'm trying to create a thread that works all the time that the server is running.
The thread needs to remove the token+client session if the client doesn't use the server (through the controllers class) passes 10 seconds.
The class of the thread:
public class ClientSessionCleaner implements Runnable {
private boolean run = true;
private Map<String, ClientSession> tokensMap;
public ClientSessionCleaner() {
/*Empty*/
}
#Autowired
public ClientSessionCleaner(#Qualifier("tokens") Map<String, ClientSession> tokensMap) {
this.tokensMap = tokensMap;
}
#Override
public void run() {
HashMap<String, ClientSession> copy = new HashMap<>(tokensMap);
do {
CleanMap(copy);
}while (run);
}
private void CleanMap(HashMap<String, ClientSession> copy) {
copy.forEach((k, v) -> {
if (System.currentTimeMillis() - v.getLastAccessMillis() == 10 * 1_000){
copy.remove(k);
}
});
}
I'm starting the thread in the main class, it is ok?
public static void main(String[] args) {
SpringApplication.run(CouponSystemApplication.class, args);
ClientSessionCleaner cleaner = new ClientSessionCleaner();
Thread thread =new Thread(cleaner);
thread.start();
}
When I'm starting the server I'm getting this:
Exception in thread "Thread-178" java.lang.NullPointerException
at java.base/java.util.HashMap.putMapEntries(HashMap.java:496)
at java.base/java.util.HashMap.<init>(HashMap.java:485)
at com.Avinadav.couponsystem.rest.login.ClientSessionCleaner.run(ClientSessionCleaner.java:25)
at java.base/java.lang.Thread.run(Thread.java:834)
The tokens map:
#Configuration
public class RestConfiguration {
#Bean(name = "tokens")
public Map<String, ClientSession> tokensMap() {
return new HashMap<>();
}
}
I don't know if the thread code is ok (?) and what I should do to make the thread work.
I'm new with threads,
thx for all the help!
If I understand you correctly, it seems like you're trying to implement some kind of a cleanup service for outdated ClientSessions. Is that right?
If so, your Runnable can actually be a #Component in which a #Scheduled annotation will define a periodic procedure in which the cleaning will take place.
For more info about Scheduling, check out the The #Scheduled Annotation in Spring
Your use-case may fit the functionality of a popular caching library like Caffeine or Google Guava, because it has support for maps with time-based eviction and it seems to be me that's what you're trying to accomplish.
LoadingCache<String, ClientSession> tokensMap = Caffeine.newBuilder()
.expireAfterAccess(10, TimeUnit.SECONDS)
.build();
For more complex logic use LoadingCache#expireAfter. Using a library like this will prevent you from having to deal with complex concurrency issues.

Singleton and Multithread in SpringBoot. Is it really multi thread?

Since I am not working specifically on multi threads, the questions can be low level or even silly, please excuse me =)
Here is my code call flow like;
MessageNotificationJobExecutionConfig -> AsyncMessageNotificationJobExecutor -> NotificationJobExecutor.execute()
MessageNotificationJobExecutionConfig (finds the objects to process) and calls AsyncMessageNotificationJobExecutor inside the loop
AsyncMessageNotificationJobExecutor has #Async("messageNotificationTaskExecutor") annotation over the execute() method.
AsyncMessageNotificationJobExecutor.execute() method calls NotificationJobExecutor.execute()
messageNotificationTaskExecutor is an instance of ThreadPoolTaskExecutor
Here is my question;
If am not wrong as default NotificationJobExecutor has a singletone instance.
Even if AsyncMessageNotificationJobExecutor work async and use thread pool task executor, all thread call only NotificationJobExecutor instance (singletone).
I am not sure, I may misunderstand that Thread_1 calls NotificationJobExecutor.execute() and until this thread finish its job other thread wait for Thread_1. Is my inference correct ?
I think even if it looks multi thread actually it works singletone
#Component("messageNotificationTaskExecutor")
public class MessageNotificationThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
#Value("${message.notification.task.executor.corePoolSize}")
Integer corePoolSize;
#Value("${message.notification.task.executor.maxPoolSize}")
Integer maxPoolSize;
#Value("${message.notification.task.executor.queueCapacity}")
Integer queueCapacity;
public MessageNotificationThreadPoolTaskExecutor() {
super();
}
#PostConstruct
public void init() {
super.setCorePoolSize(corePoolSize);
super.setMaxPoolSize(maxPoolSize);
super.setQueueCapacity(queueCapacity);
}
}
#Configuration
public class MessageNotificationJobExecutionConfig {
protected Logger log = LoggerFactory.getLogger(getClass());
#Autowired
AsyncMessageNotificationJobExecutor asyncMessageNotificationJobExecutor;
#Autowired
MessageNotificationThreadPoolTaskExecutor threadPoolTaskExecutor;
#Autowired
JobExecutionRouter jobExecutionRouter;
#Autowired
NotificationJobService notificationJobService;
private Integer operationType = OperationType.ACCOUNT_NOTIFICATION.getValue();
#Scheduled(cron = "${message.notification.scheduler.cronexpression}")
public void executePendingJobs() {
List<NotificationJob> nextNotificationJobList = notificationJobService.findNextJobForExecution(operationType, 10);
for (NotificationJob nextNotificationJob : nextNotificationJobList) {
if (threadPoolTaskExecutor.getActiveCount() < threadPoolTaskExecutor.getMaxPoolSize()) {
asyncMessageNotificationJobExecutor.execute(nextNotificationJob);
}
}
}
}
#Service
public class AsyncMessageNotificationJobExecutor {
#Autowired
NotificationJobExecutor notificationJobExecutor;
#Autowired
NotificationJobService notificationJobService;
#Async("messageNotificationTaskExecutor")
public void execute(NotificationJob notificationJob) {
notificationJobExecutor.execute(notificationJob);
}
}
#Component
public class NotificationJobExecutor implements JobExecutor {
#Override
public Integer getOperationType() {
return OperationType.ACCOUNT_NOTIFICATION.getValue();
}
#Override
public String getOperationTypeAsString() {
return OperationType.ACCOUNT_NOTIFICATION.name();
}
#Override
public void execute(NotificationJob notificationJob) {
// TODO: 20.08.2020 will be execute
}
}
In the scenario you created you have all singleton instances. But the flow looks something like this:
call to executePendingJobs in MessageNotificationJobExecutionConfig
iterate over each NotificationJob sequentially (so this is waiting)
call to execute in AsyncMessageNotificationJobExecutor which will add a execution to the messageNotificationTaskExecutor sequential (thus blocking) to the thread pool
execute the job created in step 3 in a separate thread (so this actually executes your method in AsyncMessageNotificationJobExecutor
a blocking call to the execute method in NotificationJobExecutor
The 'magic' happens in step 3, where rather then executing the method Spring will add a job to the messageNotificationTaskExecutor which wraps the call to step 4. This causes the call for step 4 to happen asynchronous and thus multiple calls to the same instance can occur at the same time. So make sure this object is stateless.

How to run two code segments concurrently: one returns a String the other returns void

My frontend is timing out (504 error) when calling my backend service. This is because my backend service takes ~6 minutes to finish running. I want to return a response to the front-end within a minute and have my backend code continue running after the response is given to the frontend.
I want to use concurrency to run two code segments. One thread will return a String to the frontend, the other thread will call the code that takes around 5 minutes to run on my server.
I want my solution to be simple as this seems like a simple problem to fix, so I am using the simple Executor class from java.util.concurrent
I made my Invoker class as followed:
public class Invoker implements Executor {
#Override
public void execute(Runnable r) {
r.run();
}
}
In my actual code, I have
import java.util.concurrent.Executor;
import com.dcc.standalone.Invoker;
public String aCoolFunction() {
String status = "good job, homie";
Executor executor = new Invoker();
executor.execute( () -> {
// Call this part of the code that takes 5 minutes to run CODE_A
});
return status;
}
I expect status to be returned at the same time CODE_A starts running. Instead, the code runs sequentially as before, i.e., status is returned after CODE_A runs.
maybe use a CompletableFuture?
Setup a ThreadPoolTaskExecutor.
#Configuration
#EnableAsync
public class SpringAsyncConfig {
#Bean(name = "threadPoolTaskExecutor")
public Executor threadPoolTaskExecutor() {
return new ThreadPoolTaskExecutor();
}
}
Define your function
public String aCoolFunction() {
String status = "good job, homie";
someAsyncFunction();
return status;
}
Define a async long running function.
#Async
public void someAsyncFcuntion() {
// Call this part of the code that takes 5 minutes to run CODE_A
}
run your cool function somewhere in a CompletableFuture
String result CompletableFuture.supplyAsync(() -> aCoolFunction()).get();
I'm writing from mobile, but this is what i could come up with from the top of my head.
Declare #Async or create a threadpool using Executors for the service field you want to use. ex)
#Service
public class SimpleService {
private ExectorService pool = Executors.newFixedThreadPool(10);
public String someThing() {
String status = "good job, homie";
pool.execute(() -> {
// other logic
})
return status;
}
}

How to find future in Spring ThreadPoolTaskScheduler

I'm using Spring ThreadPoolTaskScheduler and I need to find and cancel future by some condition.
is it right to have a ScheduledFuture field in Runnable task and collect tasks into ArrayList? Should I use CopyOnWriteArrayList?
class Task implements Runnable {
public Task(int id) {
this.id = id;
}
private final int id;
private ScheduledFuture future;
public void setFuture(ScheduledFuture future) {
this.future = future;
}
public ScheduledFuture getFuture() {
return future;
}
public int getId() {
return this.id;
}
public void run() {
System.out.println(this.id);
}
}
#Service
#RequiredArgsConstructor
public class ServiceTest {
private final ThreadPoolTaskScheduler threadPoolTaskScheduler;
private final ArrayList<Task> tasks = new ArrayList<Task>();
#PostConstruct
public void registerTasks() {
for (int i = 0; i < 3; i++) {
Task task = new Task(i);
ScheduledFuture future = threadPoolTaskScheduler.schedule(task, new
PeriodicTrigger(100));
task.setFuture(future);
tasks.add(task);
}
}
public void stopTask(int id) {
Iterator<Task> it = tasks.iterator();
while(it.hasNext()) {
Task task = it.next();
if (task.getId() == id) {
task.getFuture().cancel(false);
it.remove();
}
}
}
}
is it right to have a ScheduledFuture field in Runnable task?
From purely technical standpoint, I don't think there is any issue in storing a ScheduledFuture in the instance of the Task class which is implementing Runnable, because your Task class instance will just store it as state information, which you can use later in your code like you have done in the stopTask() method. Also, just to note here you are using a PeriodicTrigger which means the thread will keep on executing after the 100 ms time interval provided, unless the task is cancelled.
NOTE: Please ensure that your actual run() method does not change class variable future in any way. (although this is not done in the question, your real code should also not have any such changes to the future variable from inside the run() method)
collect tasks into ArrayList? Should I use CopyOnWriteArrayList?
There is no harm in using an ArrayList here, as you are using this out of any multi-threaded context. If this list was supposed to be used in a multi-threaded context, then you could probably think of using CopyOnWriteArrayList. To be precise, your ServiceTest class is having a registerTasks() method which is modifying/accessing your list, and this method is not being invoked in a multi-threaded manner as per the code shown in the question. So, there is no need for CopyOnWriteArrayList here.
Also, if needed, you could also check whether the task was cancelled or not using the returned boolean value from the call to cancel method. You may want to use it to do any further actions.
UPDATE:
Oh well, I overlooked that modification via the iterator. I agree that the stopTask() method could be accessed in a multi-threaded manner if it can be invoked via a REST Controller. However, CopyOnWriteArrayList does not support remove() method on its iterator() just in case you are thinking of using that. Also, using CopyOnWriteArrayList is only advisable if you have a majority of read operations than write operations on the list. It is actually meant for safe traversals where majority are read operations using the iterator. I would suggest synchronizing stopTask() method or using Collections.synchronizedList(tasks)

Categories