Skip to content
Snippets Groups Projects
atomicIncrement.worksheet.sc 1.79 KiB
Newer Older
import java.util.concurrent.atomic.AtomicInteger
import collection.parallel.CollectionConverters.*

// This demo should help understanding the `add` method implementation in
// src/main/scala/midterm22/Part8.scala.

// TL;DR: it demonstrate that `+=` is not atomic.

var id: Int = 0

val l =
  for _ <- (0 until 10).par yield
    id += 1
    id

l
l.distinct.size

// Try to execute the worksheet several times (by saving again and again). We
// might get a number of distinct elements not equal to 10. Why?
//
// First, note that `id += 1` is equivalent to `val tmp = id; id = tmp + 1`.
// I.e. the read and write are two separate operations; the `+=` operation is
// not *atomic*.
//
// Then, consider the following execution:
// - T1: reads 0 from id and store it in its local `tmp` var.
// - T2: does the same: reads 0 from id and store it in its local `tmp` var.
// - T1: computes `tmp + 1 == 1`, stores it in `id` and returns `1`
// - T2: computes `tmp + 1 == 1`, stores it in `id` and returns `1`
//
// Now we have two times the ID 1!

// The code we wrote before is equivalent to:

var id2: Int = 0
val l2 =
  for _ <- (0 until 10).par yield
    val tmp = id2
    id2 = tmp + 1
    val tmp2 = id2
    tmp2

l2
l2.distinct.size // *

// How to make it correct? Use an atomic integer!

val id3 = AtomicInteger()

val l3 =
  for _ <- (0 until 10).par yield id3.incrementAndGet()

l3
// Now we are sure that this will always be 10.
l3.distinct.size // *

assert(l3.distinct.size == 10)

// Or, using only what has been seen before the midterm this year: another
// solution is to use synchronize:

var id4: Int = 0
val lock = Object()

val l4 =
  for _ <- (0 until 10).par yield lock.synchronized {
    id4 += 1
    id4
  }

l4
// Now we are sure that this will always be 10.
l4.distinct.size // *

assert(l4.distinct.size == 10)