简体   繁体   中英

JAVA Keep Submitting tasks to thread pool and Shutdown threadpool when no more tasks are executing?

I am experimenting with JAVA threadpools and want to know if there is anyway i can shut-down threadpool when no more threads are submitted and submit thread when one thread finish execution.

To be more clear, i have a THREAD_LIMIT variable of Integer type which is the maximum no of threads can execute parallel and recursive algorithm which suppose to checks the active no of threads before calling itself. if the active no of threads is less than thread limit, it will submit a new thread to threadpool else calls the recursion on same thread.

The problems i am facing is to keep track of active threads and shutting down the threadpool when no new threads are submitted. I want to gain maximum performance out of my code using multi-threading.

I followed this tutorial to create my own threadpool and used

   public int getTaskQueueSize() {
    return taskQueue.size();
   }

in ThreadPool class to get active no of threads.

In main class i am using

     void shutdownExecutor() {
    while (tp.getTaskQueueSize() != 0) {
          try {
            Thread.sleep(100);
        } catch (InterruptedException ex) {
            Logger.getLogger(HamiltonPaths.class.getName()).log(Level.SEVERE, null, ex);
        }
         //  System.out.println("Not STopping tp");
    }
    tp.shutdown();
    // System.out.println("Stopped tp");

}

in main class to shutdown the Threadpool. But after sometime it stop spawning new threads. But the one thread keep doing the work in recursion.

Update 1: Code Added So i found out that submitting tasks to threadpool is working fine. But i accidently added a bug with recent change in code, which prevents me submitting more tasks to thread pool and shutting down the thread pool from shutdownExecutor() function as tp.getTaskQueueSize() is returning the initial size of the que or tasks are not getting removed from que.

I am using the following logic to decide whether to submit new task or keep working in recursion.

if ((this.tp.getTaskQueueSize() < threadLimit) && (!this.tp.isPoolShutDownInitiated())) {
                spawnNewThread(new PTask(cp, get));//Class that implements the Runnable and do the same thing as the function called in below statement.

            } else {
                PPath(cp, get);// call to the function
            }

BlockingQueueCustom.java

 package threadpool;

 abstract interface BlockingQueueCustom<E>
 {
   public abstract void put(E paramE)
     throws InterruptedException;

   public abstract E take()
     throws InterruptedException;

   public abstract int size();
 }

LinkedBlockingQueueCustom.java

package threadpool;

import java.util.LinkedList;
import java.util.List;

class LinkedBlockingQueueCustom<E>
    implements BlockingQueueCustom<E> {

private List<E> queue;
private int maxSize;

public LinkedBlockingQueueCustom(int maxSize) {
    this.maxSize = maxSize;
    this.queue = new LinkedList();
}

public synchronized void put(E item)
        throws InterruptedException {
    if (this.queue.size() == this.maxSize) {
        wait();
    }

    this.queue.add(item);
    notifyAll();
}

public synchronized E take()
        throws InterruptedException {
    if (this.queue.isEmpty()) {
        wait();
    }

    notifyAll();
    if (this.queue.isEmpty()) {
        return null;
    }
    return (E) this.queue.remove(0);
}

public synchronized int size() {
    return this.queue.size();
}
}

ThreadPool.java

package threadpool;

import java.util.logging.Level;
import java.util.logging.Logger;

public class ThreadPool {

private BlockingQueueCustom<Runnable> taskQueue;
int size = 0;
int taskExecuted = 0;
ThreadPoolsThread[] threadPoolsThread;

public int getTaskExecuted() {
    return this.taskExecuted;
}

public synchronized void dectaskExec() {
    this.taskExecuted -= 1;
}

public int getSize() {
    return this.size;
}

public BlockingQueueCustom<Runnable> getTaskQueue() {
    return this.taskQueue;
}

public int getTaskQueueSize() {
    return this.taskQueue.size();
}

private boolean poolShutDownInitiated = false;

public ThreadPool(int nThreads) {
    this.taskQueue = new LinkedBlockingQueueCustom(nThreads);
    this.size = nThreads;
    this.threadPoolsThread = new ThreadPoolsThread[nThreads + 1];

    for (int i = 1; i <= nThreads; i++) {
        this.threadPoolsThread[i] = new ThreadPoolsThread(this.taskQueue, this);
        this.threadPoolsThread[i].setName("" + i);

        this.threadPoolsThread[i].start();
    }
}

public synchronized void execute(Runnable task) {
    if (this.poolShutDownInitiated) {
        try {
            throw new Exception("ThreadPool has been shutDown, no further tasks can be added");
        } catch (Exception ex) {
            Logger.getLogger(ThreadPool.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    this.taskExecuted += 1;

    try {
        this.taskQueue.put(task);
    } catch (InterruptedException ex) {
        Logger.getLogger(ThreadPool.class.getName()).log(Level.SEVERE, null, ex);
    }
}

public boolean isPoolShutDownInitiated() {
    return this.poolShutDownInitiated;
}

public synchronized void shutdown() {
    this.poolShutDownInitiated = true;
}
}

ThreadPoolsThread.java

package threadpool;

import java.util.logging.Level;
import java.util.logging.Logger;

class ThreadPoolsThread
    extends Thread {

private BlockingQueueCustom<Runnable> taskQueue;
private ThreadPool threadPool;

public ThreadPoolsThread(BlockingQueueCustom<Runnable> queue, ThreadPool threadPool) {
    this.taskQueue = queue;
    this.threadPool = threadPool;
}

public void run() {
    for (;;) {
        Runnable runnable = null;
        while ((!this.threadPool.isPoolShutDownInitiated()) && (this.taskQueue.size() == 0)) {
        }

        if ((this.threadPool.isPoolShutDownInitiated()) && (this.taskQueue.size() == 0)) {
            break;
        }

        try {
            runnable = (Runnable) this.taskQueue.take();
        } catch (InterruptedException ex) {
            Logger.getLogger(ThreadPoolsThread.class.getName()).log(Level.SEVERE, null, ex);
            break;
        }

        if (runnable == null) {
            break;
        }

        runnable.run();

        if ((this.threadPool.isPoolShutDownInitiated())
                && (this.taskQueue.size() == 0)) {
            interrupt();

            try {
                Thread.sleep(1L);
            } catch (InterruptedException ex) {
                Logger.getLogger(ThreadPoolsThread.class.getName()).log(Level.SEVERE, null, ex);
                break;
            }
        }
    }
}
}

There seems to be a fundamental misunderstanding. You do not “submit a new thread to threadpool”, you will submit tasks to an executor service (which might be implemented as thread pool). The executor service manages the threads and decides whether to start, stop or reuse them. If you configure the executor to have a maximum number of threads, it will use no more than the specified number. If you submit more tasks than available threads, these tasks will become enqueued. Hence, the size of the queue is not the number of threads.

Generally, you should not implement your own thread pool, especially when you haven't fully understood what they do. Java has built-in implementations for more than ten years now. You can just use Executors.newFixedThreadPool(threadCount) to get a thread pool executor using the specified number of threads.

If you want to enforce that tasks are not enqueued but rather always executed by the caller when all threads are busy, it's not too hard to achieve. Create a ThreadPoolExecutor explicitly, using the constructor allowing to specify a custom queue and RejectedExecutionHandler together with a SynchronousQueue and CallerRunsPolicy :

ThreadPoolExecutor e=new ThreadPoolExecutor(threadCount, threadCount,
    1, TimeUnit.MINUTES, new SynchronousQueue<>(), Executors.defaultThreadFactory(),
    new ThreadPoolExecutor.CallerRunsPolicy());

Before Java 8, you have to make the queue type explicit: new SynchronousQueue<Runnable>()
This will pass submitted tasks to an idle thread, if any, but execute it in the submitting thread otherwise. Note that when the core size and maximum size are identical, as in the example above, the timeout value is irrelevant.

If you have submitted all tasks, you can simply invoke shutDown() without additional checks. It will still execute pending tasks before stopping all threads.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM