背景
没钱上学,我在收费站上夜班,并利用互联网自学一些编码技能,希望明天能找到更好的工作或在线销售我制作的一些应用程序。漫漫长夜,顾客寥寥。
我正在将多线程作为一个主题来处理,因为我在文献中遇到了很多使用它的代码(例如 Android SDK),但我仍然觉得它晦涩难懂。
精神
在这一点上,我的方法是:尝试编写我能想到的最基本的多线程示例,将我的头撞到墙上,看看我是否可以伸展我的大脑来适应一些新颖的思维方式。我将自己暴露在我的极限中,希望能超越它们。随意疯狂批评,吹毛求疵,并指出更好的方法来做我想做的事情。
客观的
Get some advice on how to do the above, based on my efforts so far (code provided)
练习
这是我定义的范围:
定义
创建两个类,它们在数据对象的生产和消费方面协同工作。一个线程创建对象并将它们传递到共享空间,以供另一个线程获取和使用。我们称之为生产线程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...)
为了简化练习,我决定在孩子吃蛋糕的时候不让妈妈做饭。她只会等孩子吃完蛋糕,然后立即再做一个,达到一定的限度,以进行良好的育儿。练习的本质是练习Thread的信号,而不是实现任何并发。相反,我专注于完美的序列化,没有轮询或“我可以走了吗?” 检查。我想我将不得不编写后续练习,其中母亲和孩子接下来并行“工作”。
方法
让我的类实现Runnable接口,以便它们有自己的代码入口点
使用我的类作为Thread对象的构造函数参数,这些对象从程序的
main
入口点 实例化并启动通过Thread.join()确保
main
程序不会在Thread之前终止Producer
为将创建数据的次数设置限制Consumer
就将用于表示数据生产结束的标记值达成一致
Produce
日志获取共享资源的锁和数据生产/消费事件,包括工作线程的最终签核
从程序中创建一个
SharedSpace
对象,main
并在开始之前将其传递给每个工作人员在内部为每个工作人员存储
private
对对象的引用SharedSpace
Consumer
在生成任何数据之前提供防护和消息以描述准备好使用的条件Producer
在给定次数的迭代后停止Consumer
读取哨兵值后停止
代码
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 的真实世界中”会发生什么
下一步
- 我应该从这里去哪里?我在某处看到过“未来”的概念,但我可以使用一个编号的主题列表来按顺序完成,按教学顺序进行,并带有指向相关学习资源的链接
蒂诺·西诺