Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
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)