A Dozen Concurrency Pitfalls

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:

  1. val stopMe = new Runnable {
      private var stop = false
      override def run() { while (!stop) doSomething(); println("Stopped") }
      def stopTask() { stop = true }
    }
    new Thread(stopMe).start()
  2. 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
      }
    }
  3. class BackgroundTask(iters: Int) extends Runnable {
      override def run() { for (i <- 1 to iters) doSomething() }
    }
    new Thread(new BackgroundTask(1000)).run()
    println("Started backgroundTask")
  4. val items = new ConcurrentHashMap[String, Int]
     ...
    val keys = items.keySet().toArray
  5. class Stack {
      private val myLock = "LOCK"  
    
      def push(newValue: Int) {
        myLock.synchronized {
          ...
        }
      }
    }
  6. class Stack {
      ...
      def push(newValue: Int) {
        new String("LOCK").synchronized {
          ...
        }
      }
    }
  7. 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)
      }
    }
  8. 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() }
      }
    }
  9. 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()
        }
      }
    }
  10. val queue = new ArrayBlockingQueue[String](10)
    val button = new JButton("Start")
    button.addActionListener(new ActionListener {
      override def actionPerformed(event: ActionEvent) {
        queue.put(event.toString)
      }
    }
  11. 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))
        }
      }
      ...
    }
  12. 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() }