注意:我解决的问题只有教育目的,我知道我想创建的抽象容易出错等等......我不需要快速解决方案,我需要解释
在我正在阅读的书中,有一个练习说我需要实现具有以下界面的SyncVar:
class SyncVar[T] {
def get(): T = ???
def put(x: T): Unit = ???
}
我的评论:好的似乎可以理解,需要一些我可以放置或获取的同步变量。
SyncVar对象用于在两个或多个线程之间交换值。 创建时,SyncVar对象为空:
°调用get抛出异常
°Calling put为SyncVar对象添加一个值 将值添加到SyncVar对象后,我们可以说它是非空的:
°Calling get返回当前值,并将状态更改为空
°Calling put抛出异常
我的想法:这是变量,在调用get时抛出空值异常,或者当我们有值时抛出,当我们调用get时它会清除以前的值。好像我需要使用Option。
所以我提供了以下实现:
class SyncVar[T] {
var value: Option[T] = None
def get(): T = value match {
case Some(t) => this.synchronized {
value = None
t
}
case None => throw new IllegalArgumentException("error get")
}
def put(x: T): Unit = this.synchronized{
value match {
case Some(t) => throw new IllegalArgumentException("error put")
case None => value = Some(x)
}
}
def isEmpty = value.isEmpty
def nonEmpty = value.nonEmpty
}
我的评论 同时调用put和get,也有isEmpty和nonEmpty
下一个任务让我感到困惑: 上一练习中的SyncVar对象使用起来很麻烦, 由于SyncVar对象处于无效状态时的异常。实行 SyncVar对象上的一对方法isEmpty和nonEmpty。然后, 实现一个生产者线程,它将一系列数字0转移到15 到打印它们的消费者线程。
据我所知,我需要两个线程:
//producer thread that produces numbers from 1 to 15
val producerThread = thread{
for (i <- 0 until 15){
println(s"$i")
if (syncVar.isEmpty) {
println(s"put $i")
syncVar.put(i)
}
}
}
//consumer that prints value from 0 to 15
val consumerThread = thread{
while (true) {
if (syncVar.nonEmpty) println(s"get ${syncVar.get()}")
}
}
问题: 但是这个代码是由非确定性引起的,所以每次都有不同的结果,而我需要打印1到15之间的数字(按照正确的顺序)。你能解释一下我的解决方案有什么问题吗?
答案 0 :(得分:1)
首先,synchronized
中的get
太窄了。它应该围绕整个方法,例如put
(你能想到为什么吗?)。
修复后,请考虑以下情况:
producerThread
将0放入syncVar
。
producerThread
继续运行并尝试放置1. syncVar.isEmpty
返回false
所以它不会放1.它继续循环下一个i
代替。
consumerThread
获得0。
producerThread
放置2.
等。因此consumerThread
永远无法获取并打印1,因为producerThread
永远不会将其放在那里。
如果producerThread
不为空,请考虑syncVar
应该执行的操作以及consumerThread
应该执行的操作。
答案 1 :(得分:1)
感谢@Alexey Romanov,最后我实现了转移方法:
<强>解释强>
这个想法是,生成器线程检查是syncVar
是空的,如果是它放置它,否则它等待while(syncVar.nonEmpty){}
(使用忙等待,这是不好的做法,但它很重要在教育目的中了解它以及当我们离开循环(停止忙等待)时,我们放置变量并为i == 0留下for
循环。同时消费者线程忙于等待永远,并且当它是非空时读取变量
<强>解决方案:强>
def transfer() = {
val syncVar = new SyncVar[Int]
val producerThread = thread{
log("producer thread started")
for (i <- 0 until 15){
if (syncVar.isEmpty) {
syncVar.put(i)
} else {
while (syncVar.nonEmpty) {
log("busy wating")
}
if (syncVar.isEmpty) {
syncVar.put(i)
}
}
}
}
val consumerThread = thread{
log("consumer thread started")
while (true) {
if (syncVar.nonEmpty) {
syncVar.get()
}
}
}
}