javamultithreadingconcurrencycontention

How to make writing method thread safe?


I have multiple threads to call one method in writing contents from an object to file, as below: When I use 1 thread to test this method, the output into my file is expected. However, for multiple threads, the output into the file is messy. How to make this thread safe?

void (Document doc, BufferedWriter writer){
       Map<Sentence, Set<Matrix>> matrix = doc.getMatrix();
       for(Sentence sentence : matrix.keySet()){
           Set<Matrix> set = doc.getMatrix(sentence);
           for(Matrix matrix : set){
               List<Result> results = ResultGenerator.getResult();
               writer.write(matrix, matrix.frequency());
               writer.write(results.toString());
               writer.write("\n");
           }
       }
}

Edit:

I added this line List<Result> results = ResultGenerator.getResult(). What I really want is to use multiple threads to process this method call, since this part is expensive and takes a lot of time. The writing part is very quick, I don't really need multiple threads.

Given this change, is there a way to make this method call safe in concurrent environment?


Solution

  • I am not well versed in Java so I am going to provide a language-agnostic answer.

    What you want to do is to transform matrices into results, then format them as string and finally write them all into the stream.

    Currently you are writing into the stream as soon as you process each result, so when you add multi threads to your logic you end up with racing conditions in your stream.

    You already figured out that only the calls for ResultGenerator.getResult() should be done in parallel whilst the stream still need to be accessed sequentially.

    Now you only need to put this in practice. Do it in order:

    I suspect the Java 8 provides some tools to make everything in a functional-way, but as said I am not a Java guy so I cannot provide code samples. I hope this explanation will suffice.

    @edit

    This sample code in F# explains what I meant.

    open System
    
    // This is a pretty long and nasty operation!
    let getResult doc =
        Threading.Thread.Sleep(1000)
        doc * 10
    
    // This is writing into stdout, but it could be a stream...
    let formatAndPrint =
        printfn "Got result: %O"
    
    [<EntryPoint>]
    let main argv =
        printfn "Starting..."
    
        [| 1 .. 10 |] // A list with some docs to be processed
        |> Array.Parallel.map getResult // Now that's doing the trick
        |> Array.iter formatAndPrint
    
        0