Java守护线程创建与运行示例:自动清理过期事件

阿澈
阿澈
发布时间:2026-10-14 | 5人浏览过
原创

在Java并发编程中,守护线程主要用于为其他线程提供辅助服务。本文将通过一个具体的队列清理示例,演示如何创建守护线程、其与用户线程的生命周期关联,以及在程序结束时的自动终止行为。

结果或目标

本示例旨在构建一个模拟系统:多个“用户线程”不断向双端队列添加带时间戳的事件,而一个“守护线程”在后台持续监控队列,自动删除创建时间超过10秒的过期事件。通过观察控制台输出,可以验证守护线程如何在不阻塞主程序退出的前提下执行维护任务。

准备条件

确保开发环境已配置Java JDK(示例基于Java 7+语法,但核心API兼容更高版本)。主要涉及的类包括:Event(数据结构)、WriterTask(生产者)、CleanerTask(守护消费者)和Main(启动器)。

真实操作顺序

第1步:定义事件数据结构

目的:创建用于存储事件信息的基础类。
动作:声明Date类型的日期属性和String类型的事件描述属性,并提供对应的Getter/Setter方法。
代码实现:

package com.diguage.books.concurrencycookbook.chapter1.recipe7;

import java.util.Date;

/**
 * 事件信息类
 */
public class Event {
    private Date date;
    private String event;

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    public String getEvent() {
        return event;
    }

    public void setEvent(String event) {
        this.event = event;
    }
}

第2步:编写用户线程(事件写入者)

目的:创建一个普通线程(非守护),负责周期性地向队列添加事件。
入口:实现Runnable接口。
重要参数:循环100次,每次间隔1秒。
动作:

package com.diguage.books.concurrencycookbook.chapter1.recipe7;

import java.util.Date;
import java.util.Deque;
import java.util.concurrent.TimeUnit;

public class WriterTask implements Runnable {
    private Deque<Event> deque;

    public WriterTask(Deque<Event> deque) {
        this.deque = deque;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            Event event = new Event();
            event.setDate(new Date());
            event.setEvent(String.format("The thread %s has generated an event",
                    Thread.currentThread().getId()));
            deque.addFirst(event);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

第3步:构建守护线程(事件清理者)

目的:创建守护线程,用于清理旧数据。
关键动作:在构造函数中调用setDaemon(true)。
注意:此方法必须在start()之前调用,否则将抛出IllegalThreadStateException。
代码实现:

package com.diguage.books.concurrencycookbook.chapter1.recipe7;

import java.util.Date;
import java.util.Deque;

public class CleanerTask extends Thread {
    private Deque<Event> deque;

    public CleanerTask(Deque<Event> deque) {
        this.deque = deque;
        // 设置为守护线程
        setDaemon(true);
    }

    @Override
    public void run() {
        while (true) {
            Date date = new Date();
            clean(date);
        }
    }

    /**
     * 删除超过10秒的过期事件
     */
    private void clean(Date date) {
        long difference;
        boolean delete;

        if (deque.size() == 0) {
            return;
        }

        delete = false;
        do {
            Event e = deque.getLast();
            difference = date.getTime() - e.getDate().getTime();
            // 如果时间差超过10000毫秒(10秒),则删除
            if (difference > 10000) {
                System.out.printf("Cleaner: %s\n", e.getDate());
                deque.removeLast();
                delete = true;
            }
        } while (difference > 10000);

        if (delete) {
            System.out.printf("Cleaner: Size of the queue: %d\n", deque.size());
        }
    }
}

第4步:主程序启动与调度

目的:初始化共享队列,启动3个写入线程和1个清理守护线程。
动作:

package com.diguage.books.concurrencycookbook.chapter1.recipe7;

import java.util.ArrayDeque;
import java.util.Deque;

public class Main {
    public static void main(String[] args) {
        Deque<Event> deque = new ArrayDeque<>();
        WriterTask writer = new WriterTask(deque);
        
        // 启动3个用户线程
        for (int i = 0; i < 3; i++) {
            Thread thread = new Thread(writer);
            thread.start();
        }

        // 启动1个守护线程
        CleanerTask cleaner = new CleanerTask(deque);
        cleaner.start();
    }
}

完成判断

执行程序后,控制台应开始输出类似以下内容: - 每隔约3秒,清理线程打印出被删除事件的时间戳。 - 同时打印当前队列剩余大小(通常在27到30之间波动)。 - 当所有3个写入线程结束后(约100秒后),即使清理线程仍在运行,JVM也会因为不存在任何用户线程而立即退出,这体现了守护线程的非阻塞性特征。

原文有依据的排错

  • 异常处理:如果在CleanerTask的run()中抛出未捕获异常,由于它是守护线程,它可能直接终止而不会触发全局异常处理器。建议在clean()内部使用try-catch保护逻辑。
  • 状态修改限制:牢记setDaemon()只能在线程处于NEW状态时调用。一旦start()被调用,再次调用该方法会导致运行时异常。
  • 并发安全:本示例中的ArrayDeque并非线程安全容器。在生产环境中,若多个线程同时读写,应考虑使用ConcurrentLinkedDeque或加锁机制,以避免ConcurrentModificationException。

简要总结

守护线程是Java线程模型中的重要组成部分,专为后台辅助任务设计。通过上述示例,我们学习了如何使用setDaemon(true)创建守护线程,以及它们如何在所有用户线程结束后自动死亡。这种机制常用于垃圾回收、心跳检测等不需要保证完成且不应阻止程序退出的场景。以上就是Java守护线程创建与运行示例的详细内容,更多关于Java并发编程的资料请关注本站其它相关文章!

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表