public final class BlockingThreadPool
extends Object
implements AutoCloseable
This implementation is required because the standard Java library doesn't provide a usable built-in thread pool implentation.
Java 8 has the parallel()
stream method which does something similar, but it has well documented design flaws that make it virtually unusable in real-world applications.
This class is used by the WebBot class to crawl a website using multiple threads.
try (BlockingThreadPool blockingThreadPool=new BlockingThreadPool()) {
for (final DataItem dataItem : dataItems) blockingThreadPool.submit(()->dataItem.process());
}
uiWidget.setProgressBarPercentage(0); // uiWidget is an example user interface object, not defined in this library
final AtomicInteger processedCount=new AtomicInteger(0);
try (BlockingThreadPool blockingThreadPool=new BlockingThreadPool()) {
for (final DataItem dataItem : dataItems) {
blockingThreadPool.submit(()->{
dataItem.process();
uiWidget.setProgressBarPercentage(100*processedCount.incrementAndGet()/dataItems.size());
});
}
}
| Constructor and Description |
|---|
BlockingThreadPool()
Constructs a new
BlockingThreadPool with debugging ability and the default maximum thread count. |
BlockingThreadPool(boolean debug)
Constructs a new
BlockingThreadPool with the specified debugging ability and default maximum thread count. |
BlockingThreadPool(int maxThreadCount)
Constructs a new
BlockingThreadPool with debugging ability and the specified maximum thread count. |
BlockingThreadPool(int maxThreadCount,
boolean debug)
Constructs a new
BlockingThreadPool with the specified maximum thread count and debugging ability. |
| Modifier and Type | Method and Description |
|---|---|
void |
close()
Closes the thread pool and then waits for all running tasks to finish.
|
int |
getActiveThreadCount()
Returns the number of threads currently processing tasks.
|
String |
getDebugInfo()
Returns debugging information that reports the percentage of time the thread pool spent with each possible number of active threads.
|
int |
getIdleThreadCount()
Returns the number of idle threads that are available to run tasks without blocking.
|
int |
getMaxThreadCount()
Returns the maximum number of threads in the pool.
|
void |
shutdownNow(boolean wait)
Shuts down the thread pool immediately.
|
java.util.concurrent.Future<?> |
submit(Runnable task)
Submits the specified task to the thread pool.
|
public BlockingThreadPool()
BlockingThreadPool with debugging ability and the default maximum thread count.
The default thread count is java.util.concurrent.ForkJoinPool.getCommonPoolParallelism()-1
Remember to close the object when you're finished with it.
public BlockingThreadPool(boolean debug)
BlockingThreadPool with the specified debugging ability and default maximum thread count.
The default thread count is java.util.concurrent.ForkJoinPool.getCommonPoolParallelism()-1
Remember to close the object when you're finished with it.
debug - indicates whether to enable debugging information.public BlockingThreadPool(int maxThreadCount)
BlockingThreadPool with debugging ability and the specified maximum thread count.
Remember to close the object when you're finished with it.
maxThreadCount - the maximum number of threads in the pool.public BlockingThreadPool(int maxThreadCount,
boolean debug)
BlockingThreadPool with the specified maximum thread count and debugging ability.
Remember to close the object when you're finished with it.
maxThreadCount - the maximum number of threads in the pool.debug - indicates whether to enable debugging information.public java.util.concurrent.Future<?> submit(Runnable task)
If there are no available threads in the pool, this method blocks until a thread is available.
If the task returns a result, it can be retrieved by calling the
get() method of the returned
java.util.concurrent.Future object.
This method will re-throw any exception that occurred while processing the task. See the javadoc of the
Future.get() method for details.
Rather than using the returned Future object, a more common approach is for tasks to report or accumulate their results in a shared thread-safe data object,
allowing the returned Future object to be ignored.
The task should catch any thrown exception and store it in an object accessible to the main thread,
allowing the main thread to periodically check for such exceptions and take appropriate action.
It is considered a programming error for the task code not to catch all Throwable exceptions.
If the task's code does not catch an exception, a failsafe mechanism is built in to this thread pool to re-throw the exception
on the main thread when this method is called next, or after the object is closed.
Only the most recent task exception will be re-thrown in this way.
This mechanism is only intended to be a last-ditch way of alerting the user/developer that there is problem instead of allowing the exception to disappear entirely.
It should not be relied upon for normal task exception handling.
Any checked exceptions are wrapped in a RuntimeException when re-thrown by these methods.
task - the task to be run in a separate thread.java.util.concurrent.Future object encapsulating the result of the task.public void close()
The thread pool will reject the submission of new tasks after it has been closed.
If one of the completed tasks has thrown an uncaught exception, it will be re-thrown after the thread pool is closed.
See the documentation of the submit(Runnable) method for more details.
close in interface AutoCloseablepublic void shutdownNow(boolean wait)
This clears the queue, sends an interrupt signal to all running threads, and rejects the submission of new tasks.
wait - specifies whether to wait for termination of all the tasks before returning to the caller.public int getMaxThreadCount()
public int getIdleThreadCount()
public int getActiveThreadCount()
public String getDebugInfo()
This method can only be used if the debug argument of the constructor was set to true.
For pure data processing applications the ideal result is for all the threads to be active for close to 100% of the time. If that is not the case, it is likely that some long-running tasks are still running when there are no more tasks for the other threads to process. Total running time can be optimised by ensuring longer running tasks are submitted before shorter running tasks.