Skip to content

Commit 9db997d

Browse files
committed
iluwatar#590 add explanation for Thread Pool
1 parent 8982392 commit 9db997d

File tree

1 file changed

+145
-0
lines changed

1 file changed

+145
-0
lines changed

thread-pool/README.md

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,155 @@ the system spend more time creating and destroying the threads than executing
1515
the actual tasks. Thread Pool solves this problem by reusing existing threads
1616
and eliminating the latency of creating new threads.
1717

18+
## Explanation
19+
Real world example
20+
21+
> 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.
22+
23+
In plain words
24+
25+
> Thread Pool is a concurrency pattern where threads are allocated once and reused between tasks.
26+
27+
Wikipedia says
28+
29+
> 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.
30+
31+
**Programmatic Example**
32+
33+
Let's first look at our task hierarchy. We have a base class and then concrete CoffeeMakingTask and PotatoPeelingTask.
34+
35+
```java
36+
public abstract class Task {
37+
38+
private static final AtomicInteger ID_GENERATOR = new AtomicInteger();
39+
40+
private final int id;
41+
private final int timeMs;
42+
43+
public Task(final int timeMs) {
44+
this.id = ID_GENERATOR.incrementAndGet();
45+
this.timeMs = timeMs;
46+
}
47+
48+
public int getId() {
49+
return id;
50+
}
51+
52+
public int getTimeMs() {
53+
return timeMs;
54+
}
55+
56+
@Override
57+
public String toString() {
58+
return String.format("id=%d timeMs=%d", id, timeMs);
59+
}
60+
}
61+
62+
public class CoffeeMakingTask extends Task {
63+
64+
private static final int TIME_PER_CUP = 100;
65+
66+
public CoffeeMakingTask(int numCups) {
67+
super(numCups * TIME_PER_CUP);
68+
}
69+
70+
@Override
71+
public String toString() {
72+
return String.format("%s %s", this.getClass().getSimpleName(), super.toString());
73+
}
74+
}
75+
76+
public class PotatoPeelingTask extends Task {
77+
78+
private static final int TIME_PER_POTATO = 200;
79+
80+
public PotatoPeelingTask(int numPotatoes) {
81+
super(numPotatoes * TIME_PER_POTATO);
82+
}
83+
84+
@Override
85+
public String toString() {
86+
return String.format("%s %s", this.getClass().getSimpleName(), super.toString());
87+
}
88+
}
89+
```
90+
91+
Next we present a runnable Worker class that the thread pool will utilize to handle all the potato peeling and coffee
92+
making.
93+
94+
```java
95+
public class Worker implements Runnable {
96+
97+
private static final Logger LOGGER = LoggerFactory.getLogger(Worker.class);
98+
99+
private final Task task;
100+
101+
public Worker(final Task task) {
102+
this.task = task;
103+
}
104+
105+
@Override
106+
public void run() {
107+
LOGGER.info("{} processing {}", Thread.currentThread().getName(), task.toString());
108+
try {
109+
Thread.sleep(task.getTimeMs());
110+
} catch (InterruptedException e) {
111+
e.printStackTrace();
112+
}
113+
}
114+
}
115+
```
116+
117+
Now we are ready to show the full example in action.
118+
119+
```java
120+
LOGGER.info("Program started");
121+
122+
// Create a list of tasks to be executed
123+
var tasks = List.of(
124+
new PotatoPeelingTask(3),
125+
new PotatoPeelingTask(6),
126+
new CoffeeMakingTask(2),
127+
new CoffeeMakingTask(6),
128+
new PotatoPeelingTask(4),
129+
new CoffeeMakingTask(2),
130+
new PotatoPeelingTask(4),
131+
new CoffeeMakingTask(9),
132+
new PotatoPeelingTask(3),
133+
new CoffeeMakingTask(2),
134+
new PotatoPeelingTask(4),
135+
new CoffeeMakingTask(2),
136+
new CoffeeMakingTask(7),
137+
new PotatoPeelingTask(4),
138+
new PotatoPeelingTask(5));
139+
140+
// Creates a thread pool that reuses a fixed number of threads operating off a shared
141+
// unbounded queue. At any point, at most nThreads threads will be active processing
142+
// tasks. If additional tasks are submitted when all threads are active, they will wait
143+
// in the queue until a thread is available.
144+
var executor = Executors.newFixedThreadPool(3);
145+
146+
// Allocate new worker for each task
147+
// The worker is executed when a thread becomes
148+
// available in the thread pool
149+
tasks.stream().map(Worker::new).forEach(executor::execute);
150+
// All tasks were executed, now shutdown
151+
executor.shutdown();
152+
while (!executor.isTerminated()) {
153+
Thread.yield();
154+
}
155+
LOGGER.info("Program finished");
156+
```
157+
18158
## Class diagram
19159
![alt text](./etc/thread-pool.png "Thread Pool")
20160

21161
## Applicability
22162
Use the Thread Pool pattern when
23163

24164
* You have a large number of short-lived tasks to be executed in parallel
165+
166+
## Credits
167+
168+
* [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)
169+
* [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)

0 commit comments

Comments
 (0)