Search code examples
javajerseythreadpool

Why does ThreadPoolExecutor used by a webservice call not queue multiple threads?


Consider the following setup:

1. TPETestClass.java

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/")
public class TPETestClass {
    //ThreadPoolExecutor - TPE
    private ThreadPoolExecutor exec = new ThreadPoolExecutor(1, 5, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
    
    @GET
    @Path("task")
    @Produces(MediaType.TEXT_PLAIN)
    public String startTask() {
    RunnableTestClass rtc = new RunnableTestClass();
    exec.execute(rtc);
    return String.format("There are currently %1$d tasks running\n", exec.getTaskCount());
    }

}

class RunnableTestClass implements Runnable {

    @Override
    public void run() {
    for (int i =0; i < 5; i++) {
        System.out.println(i+" seconds have passed.");
        try {
        Thread.sleep(1000);
        } catch (InterruptedException e) {
        e.printStackTrace();
        }
    }
    }
    
}

2. EmbeddedServer.java

import java.net.URI;
import javax.ws.rs.core.UriBuilder;

import org.glassfish.jersey.jdkhttp.JdkHttpServerFactory;
import org.glassfish.jersey.server.ResourceConfig;

import com.sun.net.httpserver.HttpServer;

public class EmbeddedServer {
    
    public void start()
    {
    URI baseUri = UriBuilder.fromUri("http://localhost/").port(8080).build();
    
    ResourceConfig config = new ResourceConfig(TPETestClass.class);
    HttpServer server = JdkHttpServerFactory.createHttpServer(baseUri, config);
    
    }

}

And finally 3. RunTasks.java

public class RunTasks {
    public static void main(String[] args) {
    EmbeddedServer es = new EmbeddedServer();
    es.start();
    }
}

First, I run the main method in RunTasks to start up the server on my localhost. Then, I visit the endpoint http://localhost:8080/task multiple times, which should queue up multiple threads in the ThreadPoolExecutor. The behavior I want is for the number of displayed tasks to increase as I visit the link more times. However, this does not happen. Instead, the tasks remain at 1.

Further, the printed statements to console become out of order, as below:

0 seconds have passed.
1 seconds have passed.
0 seconds have passed.
2 seconds have passed.
1 seconds have passed.
0 seconds have passed.
3 seconds have passed.
2 seconds have passed.
1 seconds have passed.
4 seconds have passed.
3 seconds have passed.

What I have tried is to make the ThreadPoolExecutor a static variable. This works as I get the following results (I visited the link 5 times): enter image description here

With the console output:

0 seconds have passed.
1 seconds have passed.
2 seconds have passed.
3 seconds have passed.
4 seconds have passed.
0 seconds have passed.
1 seconds have passed.
2 seconds have passed.
3 seconds have passed.
4 seconds have passed.
...

Here are my questions.

  1. Why do I have to make the ThreadPoolExecutor static in order for it to behave as expected? Shouldn't it behave like this even without the static variable? Are multiple instances of TPETestClass somehow created?
  2. Why do the tasks always remain at 1 and seem to execute alongside one another (based on what I am seeing in the console).
  3. Is there a better way to get this desired behavior rather than making a static variable?

Solution

    1. Why do I have to make the ThreadPoolExecutor static in order for it to behave as expected? Shouldn't it behave like this even without the static variable? Are multiple instances of TPETestClass somehow created?

    No it shouldn't. There is a new instance of TPETestClass for each request.

    According to Chapter 3. JAX-RS Application, Resources and Sub-Resources:

    "By default the life-cycle of root resource classes is per-request which, namely that a new instance of a root resource class is created every time the request URI path matches the root resource."


    1. Why do the tasks always remain at 1 and seem to execute alongside one another (based on what I am seeing in the console).

    It is not clear which version of the code you are talking about. But assuming you mean the version in which exec is an instance variable, you will get a new ExecutorService for each request and each one only ever executes one task.


    1. Is there a better way to get this desired behavior rather than making a static variable?

    The link above suggests / implies two alternatives:

    • Get your Application object to create a singleton instance of TPETestClass
    • Apply the @Singleton annotation to the TPETestClass class. There are examples further down the linked page.