Build a Self‑Sustaining Multithreaded Java Task with Graceful Shutdown

This article demonstrates how to create a perpetual multithreaded Java task using a custom thread‑pool utility, supports concurrent execution of multiple long‑running jobs, and implements an elegant shutdown that drains remaining work before terminating.

Su San Talks Tech
Su San Talks Tech
Su San Talks Tech
Build a Self‑Sustaining Multithreaded Java Task with Graceful Shutdown

Hello, I am Su San! Today I will show you how to build a perpetual multithreaded Java task, extracting the multithreading logic from a company‑internal asynchronous task project.

The example is ideal for developers with some work experience who want to learn advanced concurrency techniques or directly apply them in a project.

1. Feature Description

A perpetual asynchronous task runs continuously after it starts. For example, a push‑notification task must keep consuming un‑pushed messages from a database as long as new messages arrive.

Requirements:

Execute multiple perpetual asynchronous tasks simultaneously.

Each task can use several threads to process its data.

Support graceful shutdown: after a shutdown signal, finish processing all pending data before terminating.

Key implementation points:

Each perpetual task runs in its own thread.

Sub‑tasks are processed concurrently using a thread pool.

Shutdown must notify sub‑task threads and allow both the perpetual task and its sub‑tasks to close gracefully.

2. Multithreaded Task Example

2.1 Thread Pool Utility

The TaskProcessUtil class provides a simple thread‑pool manager.

public class TaskProcessUtil {<br/>    // Each task has its own thread pool<br/>    private static Map<String, ExecutorService> executors = new ConcurrentHashMap<>();<br/><br/>    // Initialize a thread pool<br/>    private static ExecutorService init(String poolName, int poolSize) {<br/>        return new ThreadPoolExecutor(poolSize, poolSize, 0L, TimeUnit.MILLISECONDS,<br/>                new LinkedBlockingQueue<Runnable>(),<br/>                new ThreadFactoryBuilder().setNameFormat("Pool-" + poolName).setDaemon(false).build(),<br/>                new ThreadPoolExecutor.CallerRunsPolicy());<br/>    }<br/><br/>    // Get or create a thread pool<br/>    public static ExecutorService getOrInitExecutors(String poolName, int poolSize) {<br/>        ExecutorService executorService = executors.get(poolName);<br/>        if (executorService == null) {<br/>            synchronized (TaskProcessUtil.class) {<br/>                executorService = executors.get(poolName);<br/>                if (executorService == null) {<br/>                    executorService = init(poolName, poolSize);<br/>                    executors.put(poolName, executorService);<br/>                }<br/>            }<br/>        }<br/>        return executorService;<br/>    }<br/><br/>    // Release thread‑pool resources<br/>    public static void releaseExecutors(String poolName) {<br/>        ExecutorService executorService = executors.remove(poolName);<br/>        if (executorService != null) {<br/>            executorService.shutdown();<br/>        }<br/>    }<br/>}

This utility handles pool creation, retrieval, and cleanup.

2.2 Single Task Implementation

The Cat class represents the data to be processed.

@Data<br/>@Service<br/>public class Cat {<br/>    private String catName;<br/>    public Cat setCatName(String name) {<br/>        this.catName = name;<br/>        return this;<br/>    }<br/>}

The ChildTask class encapsulates the perpetual task logic.

public class ChildTask {<br/>    private final int POOL_SIZE = 3; // thread‑pool size<br/>    private final int SPLIT_SIZE = 4; // data split size<br/>    private String taskName;<br/>    protected volatile boolean terminal = false; // shutdown flag<br/><br/>    public ChildTask(String taskName) { this.taskName = taskName; }<br/><br/>    // Entry point<br/>    public void doExecute() {<br/>        int i = 0;<br/>        while (true) {<br/>            System.out.println(taskName + ":Cycle-" + i + "-Begin");<br/>            List<Cat> datas = queryData();<br/>            taskExecute(datas);<br/>            System.out.println(taskName + ":Cycle-" + i + "-End");<br/>            if (terminal) { break; }<br/>            i++;<br/>        }<br/>        TaskProcessUtil.releaseExecutors(taskName);<br/>    }<br/><br/>    // Graceful shutdown<br/>    public void terminal() {<br/>        terminal = true;<br/>        System.out.println(taskName + " shut down");<br/>    }<br/><br/>    private void doProcessData(List<Cat> datas, CountDownLatch latch) {<br/>        try {<br/>            for (Cat cat : datas) {<br/>                System.out.println(taskName + ":" + cat.toString() + ",ThreadName:" + Thread.currentThread().getName());<br/>                Thread.sleep(1000L);<br/>            }<br/>        } catch (Exception e) {<br/>            System.out.println(e.getStackTrace());<br/>        } finally {<br/>            if (latch != null) { latch.countDown(); }<br/>        }<br/>    }<br/><br/>    private void taskExecute(List<Cat> sourceDatas) {<br/>        if (CollectionUtils.isEmpty(sourceDatas)) { return; }<br/>        List<List<Cat>> splitDatas = Lists.partition(sourceDatas, SPLIT_SIZE);<br/>        final CountDownLatch latch = new CountDownLatch(splitDatas.size());<br/>        for (List<Cat> datas : splitDatas) {<br/>            ExecutorService executorService = TaskProcessUtil.getOrInitExecutors(taskName, POOL_SIZE);<br/>            executorService.submit(() -> doProcessData(datas, latch));<br/>        }<br/>        try { latch.await(); } catch (Exception e) { System.out.println(e.getStackTrace()); }<br/>    }<br/><br/>    private List<Cat> queryData() {<br/>        List<Cat> datas = new ArrayList<>();<br/>        for (int i = 0; i < 5; i++) {<br/>            datas.add(new Cat().setCatName("LuoXiaoHei" + i));<br/>        }<br/>        return datas;<br/>    }<br/>}

2.3 Task Orchestration

public class LoopTask {<br/>    private List<ChildTask> childTasks;<br/><br/>    public void initLoopTask() {<br/>        childTasks = new ArrayList<>();<br/>        childTasks.add(new ChildTask("childTask1"));<br/>        childTasks.add(new ChildTask("childTask2"));<br/>        for (ChildTask childTask : childTasks) {<br/>            new Thread(childTask::doExecute).start();<br/>        }<br/>    }<br/><br/>    public void shutdownLoopTask() {<br/>        if (!CollectionUtils.isEmpty(childTasks)) {<br/>            for (ChildTask childTask : childTasks) { childTask.terminal(); }<br/>        }<br/>    }<br/><br/>    public static void main(String[] args) throws Exception {<br/>        LoopTask loopTask = new LoopTask();<br/>        loopTask.initLoopTask();<br/>        Thread.sleep(5000L);<br/>        loopTask.shutdownLoopTask();<br/>    }<br/>}

2.4 Result Analysis

The console output shows each child task processing five Cat objects per cycle, followed by graceful shutdown messages after the shutdown signal is issued. The second cycle completes remaining work before exiting, confirming the graceful‑shutdown behavior.

2.5 Source Code

GitHub repository: https://github.com/lml200701158/java-study/tree/master/src/main/java/com/java/parallel/pool/ofc

3. Final Thoughts

This classic thread‑pool example was originally written by a colleague (code‑name “Yi Hui”), whose technical level matches an Alibaba P7 engineer. The implementation is elegant and covers many concurrency concepts, making it a valuable learning resource.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendJavaconcurrencyThreadPoolmultithreadingGraceful Shutdown
Su San Talks Tech
Written by

Su San Talks Tech

Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.