4

I have a simple method that writes a line of data to a File followed by a new line that is executed asynchronously.

  def writeToFile(bw: BufferedWriter, str: String) = {
    bw.write(str)
    bw.newLine
  }

When my program runs I'm getting "mixed up" rows in the file due to the async nature of the calls. For instance...say writeToFile(bw, "foo") is executed 3 times asynchronously I may get:

correct output

foo

foo

foo

possible incorrect output

foofoo

foo

I'm able to avoid this possibility by using synchronized method like this:

  def writeToFile(bw: BufferedWriter, str: String) = synchronized {
    bw.write(str)
    bw.newLine
  }

From what I researched I can't determine how "safe" this is in regards to scaling my application. The only examples I can find using synchronized is when accessing collections, not writing to a file. My application is built in the Play! Framework 2.4.2.

Community
  • 1
  • 1
  • 2
    You are correct, synchronized is the JVM designed way to lock that function call. However, that doesn't mean whatever BufferedWriter you passed is protected during that time if you have other threads trying to use it so it might be wise to wrap and hide that object. Reference this link for more info: https://twitter.github.io/scala_school/concurrency.html – LaloInDublin Jan 13 '16 at 05:29

1 Answers1

8

I personally would create an akka actor for each BufferedWriter what will encapsulate it completely.

import java.io.BufferedWriter
import akka.actor._
import playground.BufferedWriterActor.WriteToBuffer

object BufferedWriterActor {
  val name = "BufferedWriterActor"
  def props(bw: BufferedWriter) = Props(classOf[BufferedWriterActor], bw)

  case class WriteToBuffer(str: String)
}

class BufferedWriterActor(bw: BufferedWriter) extends Actor {

  def receive: Actor.Receive = {
    case WriteToBuffer(str) =>
      bw.write(str)
      bw.newLine()
  }
}

Use it like this:

import akka.actor.{ActorSystem, Props}

object HelloWorld {
  def main(args: Array[String]): Unit = {
    val system = ActorSystem("mySystem")

    // Share this actor across all your threads.
    val myActor = system.actorOf(BufferedWriterActor.props(bw), BufferedWriterActor.name)

    // Send messages to this actor from all you threads.
    myActor ! BufferedWriterActor.WriteToBuffer("The Text")
  }
}

This will chain all calls to this buffer in a single thread.

More info on akka and its actors is here:

http://akka.io/

http://doc.akka.io/docs/akka/snapshot/scala/actors.html

Also play framework itself uses akka so you should be able to use its default ActorSystem, but I do not remember how exactly, sorry.