背景
由于缺乏学费,我在收费站上夜班工作,并利用互联网自学编程技能,希望明天能有更好的工作或者我制作的应用程序能在线销售。长夜漫漫,客人寥寥。
我正在尝试解决多线程问题,因为我在文献中(例如Android SDK)遇到了很多使用它的代码,但我仍然觉得它很模糊。
精神
我现在的方法是:尝试编写我能想到的最基本的多线程示例,稍微思考一下,看看能否使我的大脑适应一些新颖的思考方式。我暴露自己的极限,希望能够超越它们。请随意进行批评,甚至挑剔到点滴细节,指出更好的做法。
目标
根据我迄今为止的努力(提供代码),获得如何完成上述任务的建议
练习
这是我定义的范围:
定义
创建两个类,它们协同工作以生产数据对象并消耗这些对象。一个线程创建对象并将它们传递到共享空间中供另一个线程获取和消耗。我们称生产线程为Producer
,消费线程为Consumer
,共享空间为SharedSpace
。为了通过类比来消耗其他对象的生产行为,可以将其 assimilated 到这种情况下:`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...)
为了简化该练习,我决定不让母亲在孩子吃蛋糕时做饭。她只需等待孩子完成蛋糕,立即再做一个,直到一定的限制,以进行良好的育儿。这个练习的要点是练习线程的信号传递,而不是实现并发。相反,我专注于完美的串行化,没有轮询或“我可以走了吗?”的检查。我想我将不得不编写后续的练习,在其中母亲和孩子将“并行工作”。
方法
让我的类实现Runnable接口,以便它们有自己的代码入口点
使用我的类作为构造函数参数传递给Thread对象,这些对象从程序的
main
入口点实例化并启动通过Thread.join()确保
main
程序在Thread之前不会终止设置
Producer
创建数据供Consumer
使用的次数限制确定
Produce
将用于信号结束数据生产的sentinel值记录共享资源上锁和数据生产/消费事件的获取,包括工作线程的最终签署
从程序的
main
创建单个SharedSpace
对象,并在启动之前将其传递给每个工作线程在每个工作线程内部存储对
SharedSpace
对象的private
引用提供防范措施和消息,描述
Consumer
在任何数据被生产之前准备好消费的条件在给定迭代次数后停止
Producer
在读取到sentinel值后停止
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.
问题
- 以上内容是否正确?(例如,使用了正确的语言工具、正确的方法,是否包含任何愚蠢的代码等)
但它“看起来正确”吗?
即使输出“看起来不错”,我也会询问正确性,因为您无法想象在我的测试中有多少次事情出了差错,其中一次没有另一次(例如,当消费者首先启动时,当生产者在生成哨兵后从未退出等)。我已经学会了不从“成功运行”中宣称正确性。相反,我对伪并行代码非常怀疑!(按定义,此代码甚至不是并行的!)
详细答案
一个好的问题专注于仅一个请求的建议
(上面的建议),但如果您愿意,请随意提到以下其他主题的见解:
我如何在编写下一次尝试的同时测试并行代码?
哪些工具可以帮助我进行开发和调试? 考虑我使用Eclipse
如果我允许
Producer
继续生产,每次生产需要一些可变时间,而Consumer
消费任何可用的东西,这种方法是否会改变?锁定是否必须移动到其他位置?信号传递是否需要从此等待/通知范例更改?这种做事情的方法是否已经过时了,我应该学习其他东西吗?从这个收费站来看,我不知道在Java的“真实世界”中会发生什么
下一步
- 我接下来该去哪里?我已经看到了某处提到了"futures"的概念,但我可以使用一个按顺序排序的数字列表来逐个解决问题,并链接到相关的学习资源
Tino Sino
:]
另外,我开始学会如何通过提问而不惹恼任何人来利用集体互联网智慧:]
- Robottinosino