Skip to content

Instantly share code, notes, and snippets.

@rponte
Last active December 30, 2023 14:37
Show Gist options
  • Select an option

  • Save rponte/83c2542abf6fafd351ae0d2ff0646dae to your computer and use it in GitHub Desktop.

Select an option

Save rponte/83c2542abf6fafd351ae0d2ff0646dae to your computer and use it in GitHub Desktop.
THEORY: Example of a simple Single Thread Pool implementation in Java
public class HowToUseIt {
/**
* Usually we'll have a single instance per client
*/
private static final SingleThreadPool THREAD_POOL = new SingleThreadPool();
public void executeAsync() {
try {
THREAD_POOL
.runInBackground(new PrintWorker());
} catch (RejectedExecutionException e) {
throw new RuntimeException("There's a thread running already!");
}
}
static class PrintWorker implements Runnable {
@Override
public void run() {
System.out.println("executing in background...");
}
}
}
package br.com.rponte.util.threads;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
/**
* ThreadFactory que permite definir o nome das threads criadas,
* facilitando assim tracing e logging das threads.
*
* <p>
* A factory cria threads com nomes na forma de <i>pool-{poolName}-thread-{N}</i>,
* onde <i>{poolName}</i> é a string fornecida no construtor, e <i>{N}</i> é
* o número sequencial da thread criada por essa factory.
*
* Inspirada na classe <code>Executors.DefaultThreadFactory</code> do JDK6.
*
* <p>
* http://dubravsky.com/b/seven-rules-of-executorservice-usage-in-java-rule-3-name-the-threads-of-a-thread-pool
* https://stackoverflow.com/questions/6113746/naming-threads-and-thread-pools-of-executorservice
*/
public class NamedThreadFactory implements ThreadFactory {
private final String poolName;
private final AtomicInteger threadNumber = new AtomicInteger(1);
public NamedThreadFactory(String poolName) {
this.poolName = poolName;
}
@Override
public Thread newThread(Runnable r) {
String threadName = "pool-{poolName}-thread-{N}"
.replace("{poolName}", poolName)
.replace("{N}", String.valueOf(threadNumber.getAndIncrement()))
;
Thread t = new Thread(r, threadName);
t.setDaemon(false);
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
package br.com.rponte.util.threads;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* Pool de thread de tamanho máximo 1. Rejeita execução caso já
* exista uma thread em andamento.
*/
public class SingleThreadPool {
/**
* Pool de thread de tamanho 1.<br/>
* A idéia é não permitir mais do que uma execução simultanea de uma thread
*/
private final ExecutorService THREAD_POOL = new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new SynchronousQueue<Runnable>(), // rejeita tarefas excendentes
new NamedThreadFactory("my_pool")); // define nome para as threads
/**
* Executa worker em background porém não permite mais do que
* um worker por vez.
*
* Caso exista um processo em execução uma exceção <code>RejectedExecutionException</code>
* será lançada.
*/
public void runInBackground(Runnable worker) throws RejectedExecutionException {
THREAD_POOL.submit(worker);
}
}
@rponte
Copy link
Author

rponte commented Nov 16, 2023

Can We Follow a Concrete Formula?

The formula for determining thread pool size can be written as follows:

Number of threads = Number of Available Cores * Target CPU utilization * (1 + Wait time / Service time)

Number of Available Cores: This is the number of CPU cores available to your application. It is important to note that this is not > the same as the number of CPUs, as each CPU may have multiple cores.

Target CPU utilization: This is the percentage of CPU time that you want your application to use. If you set the target CPU utilization too high, your application may become unresponsive. If you set it too low, your application will not be able to fully utilize the available CPU resources.

Wait time: This is the amount of time that threads spend waiting for I/O operations to complete. This can include waiting for network responses, database queries, or file operations.

Service time: This is the amount of time that threads spend performing computation.

Blocking coefficient: This is the ratio of wait time to service time. It is a measure of how much time threads spend waiting for I/O operations to complete relative to the amount of time they spend performing computation.

Example Usage

Suppose you have a server with 4 CPU cores and you want your application to use 50% of the available CPU resources.

Your application has two classes of tasks: I/O-intensive tasks and CPU-intensive tasks.

The I/O-intensive tasks have a blocking coefficient of 0.5, meaning that they spend 50% of their time waiting for I/O operations to complete.

Number of threads = 4 cores * 0.5 * (1 + 0.5) = 3 threads

The CPU-intensive tasks have a blocking coefficient of 0.1, meaning that they spend 10% of their time waiting for I/O operations to complete.

Number of threads = 4 cores * 0.5 * (1 + 0.1) = 2.2 threads

In this example, you would create two thread pools, one for the I/O-intensive tasks and one for the CPU-intensive tasks. The I/O-intensive thread pool would have 3 threads and the CPU-intensive thread pool would have 2 threads.

@rponte
Copy link
Author

rponte commented Dec 30, 2023

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment