【发布时间】:2012-09-23 17:10:54
【问题描述】:
背景
因为没钱上学,我在收费站上夜班,并利用互联网自学一些编码技能,希望明天能找到更好的工作或在线销售我制作的一些应用程序。漫漫长夜,顾客寥寥。
我正在将多线程作为一个主题来处理,因为我在文献中遇到了很多使用它的代码(例如 Android SDK),但我仍然觉得它晦涩难懂。
精神
此时我的方法是:尝试编写我能想到的最基本的多线程示例,将我的头靠在墙上,看看我是否可以伸展我的大脑容纳一些新颖的思维方式。我将自己暴露在我的极限中,希望能超越它们。随意疯狂批评,甚至吹毛求疵,并指出更好的方法来做我想做的事情。
目标
Get some advice on how to do the above, based on my efforts so far (code provided)
练习
这是我定义的范围:
定义
创建两个类,它们在数据对象的生产和消费方面协同工作。一个 Thread 创建对象并将它们传送到共享空间,以供另一个获取和使用。我们称生产线程Producer、消费线程Consumer 和共享空间SharedSpace。生产物品供他人消费的行为可以通过类比的方式来同化:
`Producer` (a busy mum making chocolate-covered cakes for his child, up to a limit)
`Consumer` (a hungry child waiting to eat all cakes the mum makes, until told to stop)
`SharedSpace` (a kitchen table on which the cakes are put as soon as they become ready)
`dataValue` (a chocolate-dripping cake which MUST be eaten immediately or else...)
为了简化练习,我决定不让妈妈在孩子吃蛋糕的时候做饭。她只会等待孩子吃完蛋糕,然后立即再做一个,达到一定的限度,以进行良好的育儿。练习的本质是练习Threads 的信号,而不是实现任何并发。相反,我专注于完美的序列化,没有轮询或“我可以走了吗?”检查。我想我将不得不编写后续练习,其中母亲和孩子接下来并行“工作”。
方法
让我的类实现 Runnable 接口,以便它们有自己的代码入口点
将我的类用作Thread对象的构造函数参数,这些对象从程序的
main入口点实例化和启动- 确保
限制
Producer为Consumer创建数据的次数就一个 sentinel 值达成一致,
Produce将使用该值表示数据生产结束日志获取共享资源的锁和数据生产/消费事件,包括工作线程的最终注销
从程序的
main创建一个SharedSpace对象,并在启动前将其传递给每个worker在每个工作人员内部存储对
SharedSpace对象的private引用提供防护和消息来描述
Consumer在生成任何数据之前准备好使用的情况在给定的迭代次数后停止
ProducerConsumer读取哨兵值后停止它
main程序不会在Threads之前终止
代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class Consumer extends Threaded {
public Consumer(SharedSpace sharedSpace) {
super(sharedSpace);
}
@Override
public void run() {
super.run();
int consumedData = 0;
while (consumedData != -1) {
synchronized (sharedSpace) {
logger.info("Acquired lock on sharedSpace.");
consumedData = sharedSpace.dataValue;
if (consumedData == 0) {
try {
logger.info("Data production has not started yet. "
+ "Releasing lock on sharedSpace, "
+ "until notification that it has begun.");
sharedSpace.wait();
} catch (InterruptedException interruptedException) {
logger.error(interruptedException.getStackTrace().toString());
}
} else if (consumedData == -1) {
logger.info("Consumed: END (end of data production token).");
} else {
logger.info("Consumed: {}.", consumedData);
logger.info("Waking up producer to continue data production.");
sharedSpace.notify();
try {
logger.info("Releasing lock on sharedSpace "
+ "until notified of new data availability.");
sharedSpace.wait();
} catch (InterruptedException interruptedException) {
logger.error(interruptedException.getStackTrace().toString());
}
}
}
}
logger.info("Signing off.");
}
}
class Producer extends Threaded {
private static final int N_ITERATIONS = 10;
public Producer(SharedSpace sharedSpace) {
super(sharedSpace);
}
@Override
public void run() {
super.run();
int nIterations = 0;
while (nIterations <= N_ITERATIONS) {
synchronized (sharedSpace) {
logger.info("Acquired lock on sharedSpace.");
nIterations++;
if (nIterations <= N_ITERATIONS) {
sharedSpace.dataValue = nIterations;
logger.info("Produced: {}", nIterations);
} else {
sharedSpace.dataValue = -1;
logger.info("Produced: END (end of data production token).");
}
logger.info("Waking up consumer for data consumption.");
sharedSpace.notify();
if (nIterations <= N_ITERATIONS) {
try {
logger.info("Releasing lock on sharedSpace until notified.");
sharedSpace.wait();
} catch (InterruptedException interruptedException) {
logger.error(interruptedException.getStackTrace().toString());
}
}
}
}
logger.info("Signing off.");
}
}
class SharedSpace {
volatile int dataValue = 0;
}
abstract class Threaded implements Runnable {
protected Logger logger;
protected SharedSpace sharedSpace;
public Threaded(SharedSpace sharedSpace) {
this.sharedSpace = sharedSpace;
logger = LoggerFactory.getLogger(this.getClass());
}
@Override
public void run() {
logger.info("Started.");
String workerName = getClass().getName();
Thread.currentThread().setName(workerName);
}
}
public class ProducerConsumer {
public static void main(String[] args) {
SharedSpace sharedSpace = new SharedSpace();
Thread producer = new Thread(new Producer(sharedSpace), "Producer");
Thread consumer = new Thread(new Consumer(sharedSpace), "Consumer");
producer.start();
consumer.start();
try {
producer.join();
consumer.join();
} catch (InterruptedException interruptedException) {
interruptedException.printStackTrace();
}
}
}
执行日志
Consumer - Started.
Consumer - Acquired lock on sharedSpace.
Consumer - Data production has not started yet. Releasing lock on sharedSpace, until notification that it has begun.
Producer - Started.
Producer - Acquired lock on sharedSpace.
Producer - Produced: 1
Producer - Waking up consumer for data consumption.
Producer - Releasing lock on sharedSpace until notified.
Consumer - Acquired lock on sharedSpace.
Consumer - Consumed: 1.
Consumer - Waking up producer to continue data production.
Consumer - Releasing lock on sharedSpace until notified of new data availability.
Producer - Acquired lock on sharedSpace.
Producer - Produced: 2
Producer - Waking up consumer for data consumption.
Producer - Releasing lock on sharedSpace until notified.
Consumer - Acquired lock on sharedSpace.
Consumer - Consumed: 2.
Consumer - Waking up producer to continue data production.
Consumer - Releasing lock on sharedSpace until notified of new data availability.
Producer - Acquired lock on sharedSpace.
Producer - Produced: 3
Producer - Waking up consumer for data consumption.
Producer - Releasing lock on sharedSpace until notified.
Consumer - Acquired lock on sharedSpace.
Consumer - Consumed: 3.
Consumer - Waking up producer to continue data production.
Consumer - Releasing lock on sharedSpace until notified of new data availability.
Producer - Acquired lock on sharedSpace.
Producer - Produced: 4
Producer - Waking up consumer for data consumption.
Producer - Releasing lock on sharedSpace until notified.
Consumer - Acquired lock on sharedSpace.
Consumer - Consumed: 4.
Consumer - Waking up producer to continue data production.
Consumer - Releasing lock on sharedSpace until notified of new data availability.
Producer - Acquired lock on sharedSpace.
Producer - Produced: 5
Producer - Waking up consumer for data consumption.
Producer - Releasing lock on sharedSpace until notified.
Consumer - Acquired lock on sharedSpace.
Consumer - Consumed: 5.
Consumer - Waking up producer to continue data production.
Consumer - Releasing lock on sharedSpace until notified of new data availability.
Producer - Acquired lock on sharedSpace.
Producer - Produced: 6
Producer - Waking up consumer for data consumption.
Producer - Releasing lock on sharedSpace until notified.
Consumer - Acquired lock on sharedSpace.
Consumer - Consumed: 6.
Consumer - Waking up producer to continue data production.
Consumer - Releasing lock on sharedSpace until notified of new data availability.
Producer - Acquired lock on sharedSpace.
Producer - Produced: 7
Producer - Waking up consumer for data consumption.
Producer - Releasing lock on sharedSpace until notified.
Consumer - Acquired lock on sharedSpace.
Consumer - Consumed: 7.
Consumer - Waking up producer to continue data production.
Consumer - Releasing lock on sharedSpace until notified of new data availability.
Producer - Acquired lock on sharedSpace.
Producer - Produced: 8
Producer - Waking up consumer for data consumption.
Producer - Releasing lock on sharedSpace until notified.
Consumer - Acquired lock on sharedSpace.
Consumer - Consumed: 8.
Consumer - Waking up producer to continue data production.
Consumer - Releasing lock on sharedSpace until notified of new data availability.
Producer - Acquired lock on sharedSpace.
Producer - Produced: 9
Producer - Waking up consumer for data consumption.
Producer - Releasing lock on sharedSpace until notified.
Consumer - Acquired lock on sharedSpace.
Consumer - Consumed: 9.
Consumer - Waking up producer to continue data production.
Consumer - Releasing lock on sharedSpace until notified of new data availability.
Producer - Acquired lock on sharedSpace.
Producer - Produced: 10
Producer - Waking up consumer for data consumption.
Producer - Releasing lock on sharedSpace until notified.
Consumer - Acquired lock on sharedSpace.
Consumer - Consumed: 10.
Consumer - Waking up producer to continue data production.
Consumer - Releasing lock on sharedSpace until notified of new data availability.
Producer - Acquired lock on sharedSpace.
Producer - Produced: END (end of data production token).
Producer - Waking up consumer for data consumption.
Producer - Signing off.
Consumer - Acquired lock on sharedSpace.
Consumer - Consumed: END (end of data production token).
Consumer - Signing off.
问题
- 以上是否正确? (例如,它是否使用了正确的语言工具、正确的方法、是否包含任何愚蠢的代码……)
但它“看起来不错”?
即使输出“看起来不错”,我也会询问正确性,因为您无法想象在我的测试“一次”而不是“另一次”中出现了多少次错误(例如,当消费者首先开始时,当生产者在生产哨兵后永远不会退出等)。我学会了不要从“成功的运行”中声称正确。相反,我对伪并行代码变得非常怀疑! (根据定义,这个甚至不是平行的!0
扩展答案
一个很好的问题只关注one requested piece of advice(上述问题),但如果您愿意,请随时在您的回答中提及对以下其他主题的任何见解:
在编写下一次尝试时如何测试并行代码?
哪些工具可以帮助我进行开发和调试?考虑我使用 Eclipse
如果我允许
Producer继续生产,每次生产需要一些可变的时间,而Consumer消耗任何可用的东西,方法会改变吗?锁定是否必须移动到其他地方?信号是否需要改变这种等待/通知范式?这种做事方法已过时吗?我应该学习其他东西吗?从这个收费站,我不知道“在 Java 的真实世界中”会发生什么
后续步骤
- 我应该从这里去哪里?我曾在某处看到过“期货”的概念,但我可以使用主题编号列表按教学顺序进行操作,并附上相关学习资源的链接
蒂诺西诺
【问题讨论】:
-
天哪,对于一个没有上过专门的 CS 学校的人来说,你肯定认为比我认识的许多 CS 毕业生更有条理。荣誉。 :)
-
对于您的问题:从某种意义上说,它是“正确的”,您期望它做什么,对吗?使用等待和通知是正确的,但非常不鼓励。我还建议您使用“并发”API 中的一些东西。无论是诸如 BlockingQueue 之类的专用 DataStructure 还是同步工具……我想,您都会从中获得乐趣。我建议为您做的另一件事是不要等待太多。尝试“快速生产者,慢速消费者”,反之亦然,以及从这些场景中产生的结果。
-
哦,他们有时会这样告诉我。因此绰号“机器人”蒂诺西诺。我想是贬义的。
:]另外,我开始掌握如何通过提问而不惹恼任何人来利用集体互联网情报:] -
哈哈,“不要惹恼任何人”——这是我几个月来读到的最恰当的问题!
-
@Tudor: beato te che stai all'Uni e non al casello autostradale!
标签: java multithreading