You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java线程中Stack.pop()方法问题:如何实现Worker任务分配与指定格式输出

解决Worker线程任务分配与格式化输出的问题

看起来你遇到的问题主要有两个核心:任务的ID无法正确展示(默认输出了对象的哈希值),以及可能存在的多线程下任务分配的线程安全问题。下面一步步帮你解决:


1. 先修复Job对象的ID展示问题

你看到的Job@6577e002是Java对象默认的toString()输出,它由类名+@+哈希值组成。要输出任务的真实ID,你需要:

  • 确保你的Job类有一个存储任务ID的属性(比如int jobId),并提供对应的getter方法;
  • (可选)重写Job类的toString()方法,让它直接返回任务ID的字符串形式,方便后续打印。

示例Job类代码:

public class Job {
    private final int jobId;

    public Job(int jobId) {
        this.jobId = jobId;
    }

    // 获取任务ID的getter方法
    public int getJobId() {
        return jobId;
    }

    // 可选:重写toString,直接输出任务ID
    @Override
    public String toString() {
        return String.valueOf(jobId);
    }
}

2. 解决多线程下任务分配的线程安全问题

普通的Stack类是非线程安全的,多个Worker线程同时调用pop()时,可能会出现竞态条件(比如两个线程拿到同一个任务,或者抛出EmptyStackException)。推荐使用线程安全的并发集合来替代:

方案A:使用ConcurrentLinkedDeque(推荐)

这是Java并发包下的线程安全队列,性能比同步栈更好,它的poll()方法会安全地从队列头部取出元素(如果用push()添加元素,就模拟了栈的“后进先出”行为),且队列为空时返回null,避免异常。

方案B:包装同步Stack

如果你一定要用Stack,可以用Collections.synchronizedStack()包装它,让其线程安全,但要注意处理栈空时的异常。


3. 给Worker分配唯一ID并实现格式化输出

在创建Worker线程时,给每个线程分配一个唯一的ID(比如从1开始递增),然后在Worker的run()方法中,取出任务后按照你想要的格式输出日志。

示例Worker类代码:

import java.util.concurrent.ConcurrentLinkedDeque;

public class Worker extends Thread {
    private final int workerId;
    private final ConcurrentLinkedDeque<Job> jobQueue;

    // 构造函数传入Worker ID和任务队列
    public Worker(int workerId, ConcurrentLinkedDeque<Job> jobQueue) {
        this.workerId = workerId;
        this.jobQueue = jobQueue;
    }

    @Override
    public void run() {
        Job currentJob;
        // 循环取出任务,直到队列为空
        while ((currentJob = jobQueue.poll()) != null) {
            // 使用printf格式化输出,完全匹配你想要的格式
            System.out.printf("worker %d started job, worker %d completed job %d%n",
                    workerId, workerId, currentJob.getJobId());
            
            // 可选:模拟任务执行耗时
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                // 处理中断,终止线程
                Thread.currentThread().interrupt();
                break;
            }
        }
    }
}

4. 主程序示例:创建任务和Worker线程

import java.util.concurrent.ConcurrentLinkedDeque;

public class TaskScheduler {
    public static void main(String[] args) {
        // 初始化线程安全的任务队列
        ConcurrentLinkedDeque<Job> jobQueue = new ConcurrentLinkedDeque<>();
        
        // 添加示例任务(比如23个任务,ID从1到23)
        for (int i = 1; i <= 23; i++) {
            jobQueue.push(new Job(i));
        }

        // 创建并启动Worker线程(比如3个Worker,ID从1到3)
        int workerCount = 3;
        for (int i = 1; i <= workerCount; i++) {
            new Worker(i, jobQueue).start();
        }
    }
}

关键注意点

  • 线程安全优先:永远不要在多线程环境下使用非线程安全的集合(比如普通Stack、ArrayList),否则会出现各种难以调试的问题;
  • 明确获取任务ID:不要依赖默认的toString(),显式调用getJobId()更清晰,也避免后续toString()修改导致的输出变化;
  • 格式化输出:用System.out.printf()可以轻松实现你想要的字符串格式,比拼接字符串更简洁可靠。

内容的提问来源于stack exchange,提问作者Melih Anik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 12:34:45