All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.databasesandlife.util.ThreadPool Maven / Gradle / Ivy

package com.databasesandlife.util;

import java.util.ArrayDeque;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * Runs a number of {@link Runnable} tasks in a number of threads (over a number of CPU cores).
 *    

* Usage: *

 *      ThreadPool pool = new ThreadPool();
 *      pool.setThreadNamePrefix("foo"); // optional, for debugger output, default is class name
 *      pool.setThreadCount(5); // optional, default is number of CPU cores available
 *      pool.addTask(new Runnable() { ... }); // add one or more seed tasks;
 *      pool.execute(); // will start threads, execute the seed tasks, and execute any tasks they create
 * 
*

* In the case that any task throws an exception, this exception is thrown by the {@link #execute()} method. * If all tasks run to completion, the {@link #execute()} method returns with no value. *

* The difference to an {@link ExecutorService} is: *

    *
  • The processing of tasks can * add additional tasks to the queue (whereas to an {@link ExecutorService} the client adds a fixed number of tasks, * and they are then executed, without the executing tasks being able to add more tasks). * Such functionality is mandatory for web crawlers, which, during the processing of pages, discover links which * point to additional pages which require processing.

    *
  • It is impossible to forget to "shutdown" a ThreadPool and cause a leakage of threads, as is easily possible with {@link ExecutorService}. * If the {@link #execute()} method is never called then no threads are ever started and the object can be garbage collected normally. * If the {@link #execute()} method is called then that method makes sure all threads it creates are destroyed. *
* * @author This source is copyright Adrian Smith and licensed under the LGPL 3. * @see Project on GitHub */ public class ThreadPool { protected String threadNamePrefix = getClass().getSimpleName(); protected int threadCount = Runtime.getRuntime().availableProcessors(); protected final Queue tasks = new ArrayDeque<>(); protected int unfinishedTaskCount = 0; protected Exception exceptionOrNull = null; protected class RunnerRunnable implements Runnable { @Override public void run() { while (true) { final Runnable nextTaskOrNull; synchronized (ThreadPool.this) { if (unfinishedTaskCount == 0) break; // It's finished successfully if (exceptionOrNull != null) break; // It's failed, no point continuing nextTaskOrNull = tasks.poll(); } if (nextTaskOrNull != null) { try { nextTaskOrNull.run(); } catch (Exception e) { synchronized (ThreadPool.this) { exceptionOrNull = e; } } finally { synchronized (ThreadPool.this) { unfinishedTaskCount--; } } } else { // it might be that other tasks are running, and they will produce lots more tasks // so keep the thread alive and polling until all work is done. try { Thread.sleep(10); } catch (InterruptedException e) { } } } } } public void setThreadCount(int count) { threadCount = count; } public void setThreadNamePrefix(String prefix) { threadNamePrefix = prefix; } public synchronized void addTask(Runnable r) { tasks.add(r); unfinishedTaskCount++; } public void execute() { List threads = IntStream.range(0, threadCount) .mapToObj(i -> new Thread(new RunnerRunnable(), threadNamePrefix+"-thread"+i)) .collect(Collectors.toList()); for (Thread t : threads) t.start(); for (Thread t : threads) try { t.join(); } catch (InterruptedException e) { exceptionOrNull = e; } if (exceptionOrNull instanceof RuntimeException) throw (RuntimeException)exceptionOrNull; else if (exceptionOrNull != null) throw new RuntimeException(exceptionOrNull); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy