Is `lazy` thread-safe?

Continuing the discussion from [SOLVED] Using `Future` combined with `synchronized`:

Is lazy thread-safe? Can anyone clarify this?

2 Likes

Yes, it is.

class Foo {
  lazy val f = 3
}

Looking at the byte code with:

$ scalac foo.scala
$ javap -private -cp . -c Foo

reveals this:

public class Foo {
  private int f;

  private volatile boolean bitmap$0;

  private int f$lzycompute();
    Code:
       0: aload_0
       1: dup
       2: astore_1
       3: monitorenter
       4: aload_0
       5: getfield      #16                 // Field bitmap$0:Z
       8: ifne          21
      11: aload_0
      12: iconst_3
      13: putfield      #18                 // Field f:I
      16: aload_0
      17: iconst_1
      18: putfield      #16                 // Field bitmap$0:Z
      21: aload_1
      22: monitorexit
      23: goto          29
      26: aload_1
      27: monitorexit
      28: athrow
      29: aload_0
      30: getfield      #18                 // Field f:I
      33: ireturn
    Exception table:
       from    to  target type
           4    21    26   any

  public int f();
    Code:
       0: aload_0
       1: getfield      #16                 // Field bitmap$0:Z
       4: ifne          14
       7: aload_0
       8: invokespecial #24                 // Method f$lzycompute:()I
      11: goto          18
      14: aload_0
      15: getfield      #18                 // Field f:I
      18: ireturn

  public Foo();
    Code:
       0: aload_0
       1: invokespecial #28                 // Method java/lang/Object."<init>":()V
       4: return
}

As you can see, the private function responsible for computing the value for the lazy val foo is called f$lzycompute.

This function uses monitorenter / monitorexit byte code sequences to protect re-computing and setting the value of the field f.

4 Likes

I’d have taken thread safety of lazy values for granted - interestingly, I can’t find it in the lang spec. Am I missing something?

There seem to be some loose ends in this area (resolved in Scala 3).

2 Likes

Cats Eval.later is an alternative to lazy val and the docs explain why the Cats team provided an alternative. Cats: Eval

lazy vals in scala2.12 and 2.13 are threadsafe. In scala3 you will need to annotate them with @volatile.

1 Like

That was true for a while in some earlier Dotty versions, but in the current Scala 3 prereleases, the default behavior is the same as Scala 2’s, as per Make lazy vals thread-safe by default by smarter · Pull Request #6579 · lampepfl/dotty · GitHub

Unlike Scala 2, Scala 3 allows you to opt-out of thread safety with a new @threadUnsafe annotation.

5 Likes

Oh, good – I’d missed that detail, but I think that’s a better approach. Thanks for the update…