I needed
some filler material for my lectures on concurrency. I googled around for Java
concurrency pitfalls and came up with a nice mixture of golden oldies and new
ones (at least new to me). I cleaned them up and translated them into Scala
because that's what we use in the course. Here they are, for your puzzling
pleasure.
Rules of the game:
doSomething
, and ...
can do
anything reasonable, i.e. not call System.exit(0)
or use
reflection to mutate state. But they can call methods on non-private
variables val stopMe = new Runnable { private var stop = false override def run() { while (!stop) doSomething(); println("Stopped") } def stopTask() { stop = true } } new Thread(stopMe).start()
class MyTask implements Runnable { private val done = new ArrayBlockingQueue[String](1) private val stop = new AtomicBoolean public void run() { while (!stop.get()) doSomething() done.put("DONE") } public void stop() { stop.set(false) done.take() // Wait until run completes } }
class BackgroundTask(iters: Int) extends Runnable { override def run() { for (i <- 1 to iters) doSomething() } } new Thread(new BackgroundTask(1000)).run() println("Started backgroundTask")
val items = new ConcurrentHashMap[String, Int] ... val keys = items.keySet().toArray
class Stack { private val myLock = "LOCK" def push(newValue: Int) { myLock.synchronized { ... } } }
class Stack { ... def push(newValue: Int) { new String("LOCK").synchronized { ... } } }
class Stack { private var values = new Array[Int](10) private var size = 0 def push(newValue: Int) { values.synchronized { if (size > values.size) reallocate() values(size) = newValue size += 1 } } ... private def reallocate() { values = values ++ new Array[Int](values.size) } }
class Stack { private val myLock = new ReentrantLock private val cond = myLock.newCondition() ... def pop() = { myLock.lock() try { do { cond.await() } while (size == 0) size -= 1 values(size) } finally { myLock.unlock() } } }
class Stack { private val myLock = new ReentrantLock private var values = new Array[Int](10) private var size = 0 ... def write(fileName: String) { myLock.lock() val out = new PrintWriter(fileName) try { out.println(size + " " + values.mkString(" ")) } finally { out.close() myLock.unlock() } } }
val queue = new ArrayBlockingQueue[String](10) val button = new JButton("Start") button.addActionListener(new ActionListener { override def actionPerformed(event: ActionEvent) { queue.put(event.toString) } }
class Model { private val myLock = new ReentrantLock private def withMyLock(block: => Unit) { myLock.lock(); try { block } finally { myLock.unlock() ; } } private val listeners = new ArrayBuffer[ChangeListener] def addListener(l: ChangeListener) { withMyLock { listeners += l } } def removeListener(l: ChangeListener) { withMyLock { listeners -= l } } def fireListeners() { withMyLock { for (l <- listeners) l.stateChanged(new ChangeEvent(this)) } } ... }
val queue = new ArrayBlockingQueue[String](10) val formatter = new SimpleDateFormat("MMM dd HH:mm:ss") class MyTask extends Runnable { override def run() { Object result = doSomething() queue.put(formatter.format(new Date) + " " + result) } } for (i <- 1 to 10) { new Thread(new MyTask).start() }