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() }