用阻塞队列实现生产者消费者模式三(终止任务)

本文介绍了一种在生产者消费者模式下管理任务并允许用户中途终止任务的方法。通过定义一个任务管理器类,使用单例模式保存任务是否被终止的状态,实现了任务的动态终止。此外,还介绍了如何在生产者和消费者线程中检测任务终止信号,并通过毒丸任务快速结束线程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

除了正常任务处理完后结束生产者、消费者线程;用户还 可以中途强制结束任务。针对用户终止任务的情况,如何结束程序处理? 还是使用毒丸任务。

一、定义一个任务管理器类保存任务被终止的标记

/**
 * 任务管理类. 管理任务是否被终止的标识信息.
 *
 * @author elon
 * @since 1.0
 */
public class TaskManager {
    /**
     * 标识任务是否被终止
     */
    private volatile boolean isTaskTerminated = false;

    public static TaskManager instance() {
        return TaskManagerBuilder.instance;
    }

    public boolean isTaskTerminated() {
        return isTaskTerminated;
    }

    public void terminateTask() {
        isTaskTerminated = true;
    }

    public void clearTerminateFlag() {
        isTaskTerminated = false;
    }

    private static class TaskManagerBuilder {
        private static TaskManager instance = new TaskManager();
    }
}

TaskManager 是一个单例,定义了一个布尔变量isTaskTerminated 用于标记任务是否被终止了。这里定义一个单例只是为了简单说明问题,实际项目开发中这个标记信息一般是保存在数据库或者redis中,方便微服务能正常获取。每个任务也会有一个唯一的taskId标识。

二、添加终止任务接口

import com.elon.datamanager.TaskManager;
import com.elon.service.ProConsService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/v1/produce-consumer")
@Api(tags = "生产者消费者服务")
public class ProduceConsumerController {
    /**
     * 终止任务
     */
    @PostMapping("/terminate-task")
    @ApiOperation(value = "终止任务")
    public void terminateTask() {
        TaskManager.instance().terminateTask();
    }
}

三、生产者处理任务被终止的场景

public void run() {
    EnumTaskEndType taskEndType = EnumTaskEndType.COMPLETE;
    for (int i = 1; i <= taskAmount; ++i) {
        try {
            // 处理任务被用户终止的场景
            if (TaskManager.instance().isTaskTerminated()) {
                taskEndType = EnumTaskEndType.TERMINATE;
                break;
            }

            Task task = new Task();
            task.setTaskName("任务:" + i);
            blockingDeque.offer(task, 2, TimeUnit.MILLISECONDS);
            LOGGER.info("生产任务:{}", task);
        } catch (InterruptedException e) {
            LOGGER.error("创建任务异常.", e);
        }
    }

    // 放入毒丸任务已结束消费者线程
    try {
        Task task = new Task();
        task.setTaskName("任务处理结束毒丸");
        task.setTaskEndType(taskEndType);
        blockingDeque.offer(task, 2, TimeUnit.MILLISECONDS);
    } catch (InterruptedException e) {
        LOGGER.error("放入毒丸任务异常.", e);
    }
}

生产者在检测到任务被终止后停止生产,并往阻塞队列插入一个毒丸,以快速终止消费者线程。

四、在消费者线程处理任务被终止的场景

@Override
public void run() {
    while (true) {
        try {
            Task task = blockingDeque.take();
            if (task.getTaskEndType() != EnumTaskEndType.NA) {
                LOGGER.info("收到毒丸任务.{}", task);
                blockingDeque.offerFirst(task, 2, TimeUnit.MILLISECONDS);

                final int consumerAmount = blockingDeque.decrementConsumer();
                if (consumerAmount <= 0) {
                    LOGGER.info("所有消费者停止任务处理.");
                }
                return;
            }

            // 处理任务被用户终止的场景
            if (TaskManager.instance().isTaskTerminated()) {
                Task terminateTask = new Task();
                terminateTask.setTaskName("任务终止毒丸");
                terminateTask.setTaskEndType(EnumTaskEndType.TERMINATE);
                blockingDeque.offerFirst(terminateTask);
                continue;
            }

            // 消费任务(等待10秒,模拟实际业务处理时间)
            LOGGER.info("消费任务.{}", task);
            Thread.sleep(10*1000);
        } catch (InterruptedException e) {
            LOGGER.error("消费任务异常.", e);
        }
    }
}

消费者线程检测到任务被终止,创建一个毒丸任务放到队列。

五、调用接口终止执行中的任务

在这里插入图片描述

源码地址:https://github.com/ylforever/elon-producerconsumer

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值