Master Java Multithreading and Custom Thread Pools in One Guide
This article explains why creating threads repeatedly wastes resources, introduces Java's ExecutorService thread pool, details its configuration parameters, demonstrates how to create and use fixed thread pools with Runnable and Callable tasks, shows proper shutdown, and highlights common pitfalls with Future.get.
Creating and destroying threads consumes significant memory and CPU; repeatedly calling new Thread() can waste resources, cause excessive contention, and even crash the system. To avoid these problems, Java provides ExecutorService, a thread‑pool abstraction that reuses threads and controls concurrency. ExecutorService limits the maximum number of concurrent threads, improves resource utilization, prevents thread‑level contention, and offers scheduling features such as timed and periodic execution, eliminating the need for TimerTask.
One way to create a pool is to instantiate ThreadPoolExecutor directly:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)The parameters are:
corePoolSize : number of core threads that stay alive even when idle.
maximumPoolSize : upper limit of total threads; non‑core threads are terminated after keepAliveTime.
keepAliveTime : idle time after which non‑core threads are reclaimed.
unit : time unit for keepAliveTime (e.g., TimeUnit.SECONDS).
workQueue : queue that holds tasks waiting for a thread.
threadFactory : factory that creates new threads.
handler : policy invoked when the pool and queue are saturated.
A commonly used shortcut is the fixed‑size pool provided by Executors.newFixedThreadPool:
ExecutorService executorService = Executors.newFixedThreadPool(5);The pool size (here 5) should be chosen based on the application's workload.
To use the pool, define a Runnable implementation, submit instances, and let the pool manage execution:
class PrintTask implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println("线程名:" + threadName + " 开始时间:" + DateUtils.getTime());
System.out.println("系统需要业务操作3秒");
try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("线程名:" + threadName + " 结束时间:" + DateUtils.getTime());
}
}
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
executorService.submit(new PrintTask());
}The GIF below shows that with 10 tasks and 5 threads the work finishes in two 3‑second batches; reducing the loop to 5 tasks completes in a single batch.
When the work is done, the pool can be shut down with shutdown(). In many real‑world scenarios the pool remains alive for the application's lifetime; calling shutdown() allows the main thread to exit after all tasks finish. executorService.shutdown(); For tasks that need to return a result, implement Callable<String> and override call():
class CustomTask implements Callable<String> {
@Override
public String call() throws Exception {
String threadName = Thread.currentThread().getName();
System.out.println("线程名:" + threadName + " 开始时间:" + DateUtils.getTime());
System.out.println("系统需要业务操作1秒");
try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
return "threadName = " + threadName + " 结束时间:" + DateUtils.getTime();
}
}
ExecutorService executorService = Executors.newFixedThreadPool(5);
ArrayList<Future<String>> resultList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Future<String> submit = executorService.submit(new CustomTask());
resultList.add(submit);
}
resultList.forEach(result -> {
try { System.out.println(result.get()); }
catch (InterruptedException | ExecutionException e) { e.printStackTrace(); }
});The Future.get() call blocks, so it should be invoked after all submissions; calling it inside the loop would serialize execution and defeat concurrency, as illustrated by the erroneous example and its GIF.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
The Dominant Programmer
Resources and tutorials for programmers' advanced learning journey. Advanced tracks in Java, Python, and C#. Blog: https://blog.csdn.net/badao_liumang_qizhi
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
