Reputation: 23134
Code:
class Page(val txt: String, var position: Int)
val rand = new Random()
object Volatile {
val pages = for(i <- 1 to 5) yield new Page("Na" * rand.nextInt(1000) + " Batman!", -1)
@volatile var found = false
for (p <- pages) yield thread {
var i = 0
while (i < p.txt.length && !found) if (p.txt(i) == '!') {
p.position = i
found = true
} else i += 1
while(!found) {}
log(s"results: ${pages.map(_.position)}")
}
}
What is the purpose of the empty loop while(!found) {}
?
Upvotes: 1
Views: 103
Reputation: 23134
Following @Silly Freak's suggestions, I have modified the code to use synchronized
instead of volatile
:
object AntiVolatile {
val pages = for (i <- 1 to 15) yield new Page("!Na" * rand.nextInt(1000) + " Batman!", -1)
var found = Some(false)
def run(): Unit = {
for (p <- pages) yield thread {
var i = 0
var foundInThread = found.get
while (i < p.txt.length && !foundInThread)
if (p.txt(i) == '!') {
found.synchronized {
found match {
case Some(true) => foundInThread = true
case Some(false) => {
p.position = i
found = Some(true)
Thread.sleep(1)
}
case _ =>
}
}
} else i += 1
// if still not found, wait for another thread to find it.
def wait(): Unit = {
found match {
case Some(false) => wait()
case _ =>
}
}
wait()
log(s"results: ${pages.map(_.position)}")
}
}
}
No more data race:
Thread-29: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-27: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-28: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-26: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-30: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-31: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-32: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-25: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-33: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-34: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-39: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-38: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-37: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-36: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-35: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Upvotes: 0
Reputation: 4231
The first loop: while (i < p.txt.length && !found)
has two conditions for stopping: when the search space is exhausted, or when a match was found. Note that the found
variable is used in multiple threads: thread {...}
presumably spawns a new thread.
So there are several possibilities how the threads' loops can play out:
'!'
and sets found
. The first loop breaks, the second doesn't execute.found
. The first loop breaks, the second doesn't execute.'!'
.The third one is probably the one you forgot. The consequence is that results are only logged once all loops are finished.
Granted, I can't tell you why that code looks like it does: each thread, separately prints the results for all pages, i.e. if there are n
pages, n^2
results are logged. Also, all threads access a shared object without synchronization. If the goal was to find exactly one exclamation mark, the code fails. Anyway, these seem like separate issues.
Upvotes: 2
Reputation: 45806
If looks like a busy wait. It will continue to check the status of found
, and execution won't continue until found
is true.
Upvotes: 1