我正在努力学习Scala,并发现它是一种很棒的语言。我向David Pollak学习"Beginning Scala"。在第3章中有这段代码,它说明了如何编写没有同步块的多线程代码(这段代码是从书中复制的,可以从Apress site下载,我不是故意破坏任何代码这里的法律):
import java.util.concurrent.atomic.{AtomicReference => AtomR, AtomicLong} import java.util.Random import scala.collection.immutable.TreeHashMap object Multics { type MT = Map[String, Int] val info: AtomR[MT] = new AtomR(TreeHashMap.empty) val clashCnt = new AtomicLong def main(argv: Array[String]) { runThread { repeatEvery(1000) { println("Clash Count: "+clashCnt+" Total: "+ info.get.foldLeft(0)(_ + _._2)) } } for (i old + (name -> 0)} repeatEvery(ran.nextInt(100)) { doSet(info){old => old + (name -> (old(name) + 1))} cnt = cnt + 1 if (cnt != info.get()(name)) throw new Exception("Thread: "+name+" failed") } } } def runThread(f: => Unit) = (new Thread(new Runnable {def run(): Unit = f})).start def doSet[T](atom: AtomR[T])(update: T => T) { val old = atom.get if (atom.compareAndSet(old, update(old))) () else { clashCnt.incrementAndGet doSet(atom)(update) } } def repeatEvery(len: => Int)(body: => Unit): Unit = { try { while(true) { Thread.sleep(len) body } } catch { case e => e.printStackTrace; System.exit(1) } } }
根据我的理解,函数doSet
中存在潜在的饥饿问题(不幸的线程可能总是冲突并导致StackOverflowException)。我是对的,如果是的话,如何改进这些代码来解决这个问题呢?
答案 0 :(得分:3)
首先,我认为您从书籍示例中粘贴的代码遗漏了一大堆;这让你很难理解你的问题。
其次,doSet()可能会被递归多次调用,但在这种情况下不会发生StackOverflowException,因为有一个保存宽限:尾递归。 doSet()在函数末尾递归调用(在递归调用之后不再进行处理)并且不能被覆盖(在对象内定义),这使得它被优化为循环而没有任何堆栈开销。 / p>
在2.8.0中,有一个名为@scala.annotation.tailrec的注释,当在def上使用时,它将确保def内的递归调用确实是尾递归。
答案 1 :(得分:0)
它看起来像是使用Compare和Swap(http://en.wikipedia.org/wiki/Compare-and-swap)而不是锁定。
这种方法的一般想法是循环,直到你设法一致地设置值。
如果有饥饿问题我还不知道这个问题。我的猜测是理论上存在饥饿问题,但在实践中它会没问题。