发布于2026-07-04 阅读(0)
扫一扫,手机访问
在Ja va开发中,生产者-消费者模式是一个非常经典的并发协作模型。但关于它的实现方式,很多新手可能会遇到一个选择:是继承Thread类重写run(),还是采用Runnable接口?从实践来看,继承Thread这条路,大部分情况下都不推荐走,因为它带来的麻烦远多于便利。

不推荐通过继承 Thread 类并重写 run() 方法来实现生产者-消费者模式。这种方式在面向对象设计和线程安全层面都留有隐患,实际项目里几乎见不到它的身影。
生产者-消费者模式的核心命题是数据共享和协作同步,而不只是单纯地让线程跑起来。一旦用继承 Thread 的方式来做,就会触及几个非常棘手的硬伤:
Thread,就无法再继承其他业务基类,扩展性被死死卡住Thread(),创建开销大不说,管理上也是一团乱麻业界公认的做法,是把生产者和消费者作为 Runnable 接口的实现,然后交给线程池统一调度。线程间的数据通信用 BlockingQueue(比如 ArrayBlockingQueue)来解决,阻塞等待、线程安全这些问题,队列本身全部包办了。
来看一段关键代码示例:
// 共享缓冲区 BlockingQueuequeue = new ArrayBlockingQueue<>(10); // 生产者 new Thread(() -> { for (int i = 0; i < 5; i++) { try { queue.put("item-" + i); // 满了自动阻塞 System.out.println("Produced: item-" + i); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }).start(); // 消费者 new Thread(() -> { for (int i = 0; i < 5; i++) { try { String item = queue.take(); // 空了自动阻塞 System.out.println("Consumed: " + item); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }).start();
这段代码逻辑清晰,耦合度低,而且不用操心同步细节——BlockingQueue 在背后做了一切。
当然,如果你只是想研究一下多线程通信的底层原理,或者应付课堂作业,用继承 Thread 也不是不可以。但有几个关键前提必须满足:共享资源必须独立于线程类,同步块必须指向同一把锁,wait/notify 一定要配对出现在 synchronized 块内部。
class SharedBuffer {
private final List list = new ArrayList<>();
private final int capacity = 5;
public synchronized void produce(String item) throws InterruptedException {
while (list.size() == capacity) wait();
list.add(item);
System.out.println("Produced: " + item);
notifyAll();
}
public synchronized String consume() throws InterruptedException {
while (list.isEmpty()) wait();
String item = list.remove(0);
System.out.println("Consumed: " + item);
notifyAll();
return item;
}
}
class Producer extends Thread {
private final SharedBuffer buffer;
Producer(SharedBuffer b) { this.buffer = b; }
public void run() {
try {
for (int i = 0; i < 5; i++) buffer.produce("P-" + i);
} catch (InterruptedException e) { interrupt(); }
}
}
class Consumer extends Thread {
private final SharedBuffer buffer;
Consumer(SharedBuffer b) { this.buffer = b; }
public void run() {
try {
for (int i = 0; i < 5; i++) buffer.consume();
} catch (InterruptedException e) { interrupt(); }
}
}
注意,这里的核心要点是:两个子类必须共享同一个 SharedBuffer 实例,所有同步操作都围绕它展开,否则锁就会乱掉。
如果项目是基于 Ja va 8 或更高版本,建议直接用 ExecutorService 管理线程池。配合 BlockingQueue 搞定数据同步,必要时引入 Phaser、CountDownLatch 做更复杂的协调。对于那些高并发、讲究细粒度控制的场景,ReentrantLock + Condition 组合往往比 synchronized + wait/notify 更顺手,能提供更精细的唤醒策略。总而言之,选对工具,比纠结于“能不能继承 Thread”要重要得多。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8