qrius
qrius

Reputation: 631

Java concurrency: Modifying latch/ThreadGroup to achieve Executor behaviour

This question is related to my homework assignment in Java concurrency topic. I have been tasked to spawn new threads and limit them by a given concurrencyFactor. That is, keep on dispatching new threads until number of active threads is less than or equal to concurrencyFactor. If the number of active threads is equal to concurrencyFactor, program will wait until number of active threads reduce to concurrencyFactor - 1 and create a new one.

As a first approach, I am using ExecutorService and created a new fixed pool by Executors.newFixedThreadPool(concurrencyFactor); and whenever my method is called, I am just submitting a new runnable to this pool. Code for the logic is below:

    private final ExecutorService fixedPoolExecutor = Executors.newFixedThreadPool(concurrencyFactor);
    public void handleRequest(final RequestHandler handler) {
    if (handler == null) throw new IllegalArgumentException("Handler cannot be null");
    fixedPoolExecutor.submit(new Runnable() {
        @Override
        public void run() {
            handler.serviceRoutine();
        }
      });
    }

Now, the second part asks me to achieve the same goal but without using executor. I have thought of following two approaches:
1) Use countDownLatch but this latch would wait(i.e. latch.await()) until activeCount becomes 0. I want to wait only till the countDown becomes concurrencyFactor - 1.
2) Use ThreadGroup and wait until threadGroup.activeCount() < concurrencyFactor. But, the problem with this approach is how can I make the incoming request wait until the condition threadGroup.activeCount() < concurrencyFactor meets? I have used the below code for this approach:

    private final Lock lock = new ReentrantLock();
    private final ThreadGroup threadGroup = new ThreadGroup("myGroup");
    public void handleRequest(final RequestHandler handler) {
    if (handler == null) throw new IllegalArgumentException("Handler cannot be null");
    lock.lock();
    try {
        while (threadGroup.activeCount() >= concurrencyFactor) {

        }
        Thread t = new Thread(threadGroup, new Runnable() {
            @Override
            public void run() {
                handler.service();
            }
        });
        t.start();
    } finally {
        lock.unlock();
    }        
   }

Can I replace the blank while loop with some wait condition in the second approach?

Any suggestions on above approaches or advice for any new approach would be appreciated.

Upvotes: 0

Views: 159

Answers (1)

bowmore
bowmore

Reputation: 11280

I would suggest using Sempahore. The semaphore would represent the number of threads still allowed to be started. Initially it holds permits equal to the configured concurrency factor.

Before starting a new thread, the handleRequest method needs to acquire a permit from the semaphore. The threads that are started should, upon completion release that permit again.

Sample code :

private final ThreadGroup threadGroup = new ThreadGroup("myGroup");
private final Semaphore concurrencyFactor = new Semaphore(CONCURRENCY_FACTOR);

public void handleRequest(final RequestHandler handler) throws InterruptedException {
    if (handler == null) throw new IllegalArgumentException("Handler cannot be null");

    concurrencyFactor.acquire(); // Get permit

    Thread t = new Thread(threadGroup, new Runnable() {
        @Override
        public void run() {
            try {
                handler.service();
            } finally {
                concurrencyFactor.release(); // make sure to release permit
            }
        }
    });
    t.start();
}

(you may want to deal differently with possible interruption)

Upvotes: 1

Related Questions