3

I'm currently developing a web application using spring boot and I have a problem in the service layer.

I have a heavy method on my service layer. If multiple users call that same service, the applications stops due to low memory. So I want to limit the no of parallel running threads of that method only. So far I have come out with using synchronized on that method. But it will limit it to single threaded method.

@Service
public class DocumentService{

    private synchronized void doReplacement(){
       //should have limited no of multi threads (eg. 3)
    }

    private void normalMethod(){
       //no restrictions
    }

}

What can I do to achieve this task. Any help would be appreciated.

Ravindu
  • 2,408
  • 8
  • 30
  • 46
  • apply a manual lock to the method when executing, if not.... maybe [this](http://stackoverflow.com/questions/16591147/lock-a-runnable-until-finished) or [this](http://stackoverflow.com/questions/21124879/how-do-i-make-java-wait-for-a-method-to-finish-before-continuing) may help... – Jordi Castilla Jul 20 '16 at 08:11
  • 1
    Well you could create and use Executors to limit the number of threads. BTW first check if there are not memory leaks...... – Karthik Prasad Jul 20 '16 at 08:25
  • Since spring framework create all the threading in service layer, I can't handle threads manually. Is there a way?? – Ravindu Jul 20 '16 at 08:30
  • 2
    The common way of solving this problem is to wrap your service in a service provider. The provider than using a counting semaphore to ensure no more than X instances of a thing exists, and block threads that request it. The other option is to change the tomcat configuration (if you're using tomcat internally) to limit the number of concurrent threads it will handle for each request, but that may have a knock on effect on the rest of your application. – christopher Jul 20 '16 at 08:39
  • @christopher can you give me an example if possible? – Ravindu Jul 20 '16 at 09:01

1 Answers1

2

You may be better of with using some sort of request throttling (i.e. number of requests per second) than with the number of threads that can execute a method simultaneously. For instance using Guava's RateLimiter directly, or maybe event adding declarative support for with Spring's AOP.

If you still want to go with threads, my suggestion would be to use an ExecutorService:

@Service
public class DocumentService {

    private final ExecutorService executor;

    @Autowired
    public DocumentService(
        @Value("${some.config.property}") int maxConcurrentThreads) {
        // will allow only the given number of threads
        executor = Executors.newFixedThreadPool(maxConcurrentThreads);
    }

    private void doReplacementWithLimitedConcurrency(String s, int i){
        Future<?> future = executor.submit(() -> doReplacement(s, i));
        future.get(); // will block until a thread picks up the task
                      // and finishes executing doReplacement
    }

    private void doReplacement(String s, int i){
    }

    // other methods

    @PreDestroy
    public void performThreadPoolCleanup() throws Exception {
        executor.shutdown();
        executor.awaitTermination(10, TimeUnit.SECONDS); 
        executor.shutdownNow();
    }
}
Miloš Milivojević
  • 5,219
  • 3
  • 26
  • 39
  • The problem here is I have to pass some argument in the doReplacement like doReplacement(String s,Int i). Callable in submit wont allow arguments. How can I work around that? – Ravindu Jul 21 '16 at 04:30
  • Thanks Milos, I created a class as DoReplacement and implemented by Callable. And passed the values to it using a constructor. – Ravindu Jul 21 '16 at 08:09
  • 1
    That works too, although it's a bit less efficient than using a lambda. Unless you're still on Java 7, then it makes perfect sense :) – Miloš Milivojević Jul 21 '16 at 08:11
  • wow lambda to the rescue. It works. Can you please explain what you did there. I'm not familiar with () -> signs you used there. I know it's lambda. But submit is asking for Callable and you used a lambda expression there. HOW? – Ravindu Jul 21 '16 at 08:48
  • 1
    Java 8 allows you to represent functional interfaces (interfaces containing a single abstract method) with a lambda expression, where that lambda is basically a shorthand definition of that interface's method. In this case, submit also accepts a Runnable and Runnable is a functional interface so Java will interpret `() -> doReplacement(s, i)` as an implementation of Runnable. I suggest you listen to [this fantastic talk by Venkat Subramaniam](https://www.infoq.com/presentations/java8-streams-lambda) – Miloš Milivojević Jul 21 '16 at 09:16