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
相关产品推荐
相关产品推荐

