#590 add explanation for Thread Pool
This commit is contained in:
parent
8982392fea
commit
9db997d0ae
@ -15,6 +15,146 @@ the system spend more time creating and destroying the threads than executing
|
|||||||
the actual tasks. Thread Pool solves this problem by reusing existing threads
|
the actual tasks. Thread Pool solves this problem by reusing existing threads
|
||||||
and eliminating the latency of creating new threads.
|
and eliminating the latency of creating new threads.
|
||||||
|
|
||||||
|
## Explanation
|
||||||
|
Real world example
|
||||||
|
|
||||||
|
> We have a large number of relatively short tasks at hand. We need to peel huge amounts of potatoes and serve mighty amount of coffee cups. Creating a new thread for each task would be a waste so we establish a thread pool.
|
||||||
|
|
||||||
|
In plain words
|
||||||
|
|
||||||
|
> Thread Pool is a concurrency pattern where threads are allocated once and reused between tasks.
|
||||||
|
|
||||||
|
Wikipedia says
|
||||||
|
|
||||||
|
> In computer programming, a thread pool is a software design pattern for achieving concurrency of execution in a computer program. Often also called a replicated workers or worker-crew model, a thread pool maintains multiple threads waiting for tasks to be allocated for concurrent execution by the supervising program. By maintaining a pool of threads, the model increases performance and avoids latency in execution due to frequent creation and destruction of threads for short-lived tasks. The number of available threads is tuned to the computing resources available to the program, such as a parallel task queue after completion of execution.
|
||||||
|
|
||||||
|
**Programmatic Example**
|
||||||
|
|
||||||
|
Let's first look at our task hierarchy. We have a base class and then concrete CoffeeMakingTask and PotatoPeelingTask.
|
||||||
|
|
||||||
|
```java
|
||||||
|
public abstract class Task {
|
||||||
|
|
||||||
|
private static final AtomicInteger ID_GENERATOR = new AtomicInteger();
|
||||||
|
|
||||||
|
private final int id;
|
||||||
|
private final int timeMs;
|
||||||
|
|
||||||
|
public Task(final int timeMs) {
|
||||||
|
this.id = ID_GENERATOR.incrementAndGet();
|
||||||
|
this.timeMs = timeMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTimeMs() {
|
||||||
|
return timeMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("id=%d timeMs=%d", id, timeMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CoffeeMakingTask extends Task {
|
||||||
|
|
||||||
|
private static final int TIME_PER_CUP = 100;
|
||||||
|
|
||||||
|
public CoffeeMakingTask(int numCups) {
|
||||||
|
super(numCups * TIME_PER_CUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("%s %s", this.getClass().getSimpleName(), super.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PotatoPeelingTask extends Task {
|
||||||
|
|
||||||
|
private static final int TIME_PER_POTATO = 200;
|
||||||
|
|
||||||
|
public PotatoPeelingTask(int numPotatoes) {
|
||||||
|
super(numPotatoes * TIME_PER_POTATO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("%s %s", this.getClass().getSimpleName(), super.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Next we present a runnable Worker class that the thread pool will utilize to handle all the potato peeling and coffee
|
||||||
|
making.
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class Worker implements Runnable {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(Worker.class);
|
||||||
|
|
||||||
|
private final Task task;
|
||||||
|
|
||||||
|
public Worker(final Task task) {
|
||||||
|
this.task = task;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
LOGGER.info("{} processing {}", Thread.currentThread().getName(), task.toString());
|
||||||
|
try {
|
||||||
|
Thread.sleep(task.getTimeMs());
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now we are ready to show the full example in action.
|
||||||
|
|
||||||
|
```java
|
||||||
|
LOGGER.info("Program started");
|
||||||
|
|
||||||
|
// Create a list of tasks to be executed
|
||||||
|
var tasks = List.of(
|
||||||
|
new PotatoPeelingTask(3),
|
||||||
|
new PotatoPeelingTask(6),
|
||||||
|
new CoffeeMakingTask(2),
|
||||||
|
new CoffeeMakingTask(6),
|
||||||
|
new PotatoPeelingTask(4),
|
||||||
|
new CoffeeMakingTask(2),
|
||||||
|
new PotatoPeelingTask(4),
|
||||||
|
new CoffeeMakingTask(9),
|
||||||
|
new PotatoPeelingTask(3),
|
||||||
|
new CoffeeMakingTask(2),
|
||||||
|
new PotatoPeelingTask(4),
|
||||||
|
new CoffeeMakingTask(2),
|
||||||
|
new CoffeeMakingTask(7),
|
||||||
|
new PotatoPeelingTask(4),
|
||||||
|
new PotatoPeelingTask(5));
|
||||||
|
|
||||||
|
// Creates a thread pool that reuses a fixed number of threads operating off a shared
|
||||||
|
// unbounded queue. At any point, at most nThreads threads will be active processing
|
||||||
|
// tasks. If additional tasks are submitted when all threads are active, they will wait
|
||||||
|
// in the queue until a thread is available.
|
||||||
|
var executor = Executors.newFixedThreadPool(3);
|
||||||
|
|
||||||
|
// Allocate new worker for each task
|
||||||
|
// The worker is executed when a thread becomes
|
||||||
|
// available in the thread pool
|
||||||
|
tasks.stream().map(Worker::new).forEach(executor::execute);
|
||||||
|
// All tasks were executed, now shutdown
|
||||||
|
executor.shutdown();
|
||||||
|
while (!executor.isTerminated()) {
|
||||||
|
Thread.yield();
|
||||||
|
}
|
||||||
|
LOGGER.info("Program finished");
|
||||||
|
```
|
||||||
|
|
||||||
## Class diagram
|
## Class diagram
|
||||||

|

|
||||||
|
|
||||||
@ -22,3 +162,8 @@ and eliminating the latency of creating new threads.
|
|||||||
Use the Thread Pool pattern when
|
Use the Thread Pool pattern when
|
||||||
|
|
||||||
* You have a large number of short-lived tasks to be executed in parallel
|
* You have a large number of short-lived tasks to be executed in parallel
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
* [Effective Java](https://www.amazon.com/gp/product/0134685997/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0134685997&linkId=e1b9ddd5e669591642c4f30d40cd9f6b)
|
||||||
|
* [Java Concurrency in Practice](https://www.amazon.com/gp/product/0321349601/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0321349601&linkId=fbedb3bad3c6cbead5afa56eea39ed59)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user