-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathJavaSynchronization.java
More file actions
186 lines (167 loc) · 6.83 KB
/
Copy pathJavaSynchronization.java
File metadata and controls
186 lines (167 loc) · 6.83 KB
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
package Phase7_Concurrency.Multithreading;
/**
* Java Synchronization
* --------------------
* SYNCHRONIZATION is the discipline of letting only ONE thread at a time
* touch shared state, so that updates from different threads compose
* sanely. In Java the canonical primitive is the `synchronized` keyword
* — a built-in lock attached to every object (called the MONITOR or
* INTRINSIC LOCK).
* <p>
*
* Three forms of `synchronized`
* -----------------------------
* 1. Synchronized INSTANCE method - locks `this`.
* <p>
*
* public synchronized void m() { ... }
* <p>
*
* 2. Synchronized STATIC method - locks the Class object.
* <p>
*
* public static synchronized void m() { ... } // locks YourClass.class
* <p>
*
* 3. Synchronized BLOCK - locks any object reference.
* <p>
*
* synchronized (lockObj) { ... }
* <p>
*
* What synchronized guarantees
* ----------------------------
* MUTUAL EXCLUSION - only one thread at a time inside the same lock.
* VISIBILITY - on lock acquire, the thread sees every change
* made before the matching release. (JMM
* happens-before: release → subsequent acquire.)
* ORDERING - the JIT/CPU cannot reorder reads & writes across
* the lock boundaries in observable ways.
* <p>
*
* What synchronized does NOT do
* -----------------------------
* - Make individual operations atomic on their own (it's the BLOCK
* that is atomic, not the calls inside).
* - Prevent deadlock — that's on you.
* - Time out. Use ReentrantLock.tryLock if you need that.
* <p>
*
* Reentrancy
* ----------
* Java monitors are REENTRANT — the SAME thread can lock the same
* monitor multiple times without deadlocking itself. Each acquire
* needs a matching release.
* <p>
*
* Common rules
* ------------
* - Always lock the SAME object for accesses to the same state.
* - DON'T synchronize on a String literal, Integer, Boolean — pooled
* and shared with unrelated code.
* - DON'T hold a lock while calling unknown code (callbacks,
* overrides, lambdas you don't own) — invitation to deadlock.
* - Keep critical sections SHORT.
* <p>
*
* Class lock vs object lock
* -------------------------
* - Instance synchronized methods on different instances do NOT
* contend with each other.
* - Static synchronized methods on the same class ALWAYS contend.
* - An instance synchronized method and a static synchronized method
* on the SAME class lock DIFFERENT monitors and do NOT contend.
*/
public class JavaSynchronization {
/** Shared counter — unsynchronized version for contrast. */
static int unsafe = 0;
/** Same counter but bumped inside a synchronized block. */
static int safe = 0;
static final Object SAFE_LOCK = new Object();
public static void main(String[] args) throws InterruptedException {
section("1) WITHOUT synchronization — lost updates");
unsafe = 0;
Thread a1 = new Thread(() -> { for (int i = 0; i < 100_000; i++) unsafe++; });
Thread a2 = new Thread(() -> { for (int i = 0; i < 100_000; i++) unsafe++; });
a1.start(); a2.start();
a1.join(); a2.join();
System.out.println("unsafe = " + unsafe + " (expected 200000 — usually less)");
section("2) WITH synchronized block — correct count");
safe = 0;
Thread b1 = new Thread(() -> {
for (int i = 0; i < 100_000; i++) {
synchronized (SAFE_LOCK) { safe++; }
}
});
Thread b2 = new Thread(() -> {
for (int i = 0; i < 100_000; i++) {
synchronized (SAFE_LOCK) { safe++; }
}
});
b1.start(); b2.start();
b1.join(); b2.join();
System.out.println("safe = " + safe + " (always 200000)");
section("3) synchronized INSTANCE method locks `this`");
Counter c = new Counter();
Runnable bumpC = () -> { for (int i = 0; i < 100_000; i++) c.bump(); };
Thread t1 = new Thread(bumpC), t2 = new Thread(bumpC);
t1.start(); t2.start();
t1.join(); t2.join();
System.out.println("Counter instance = " + c.get() + " (200000)");
section("4) synchronized STATIC method locks the Class object");
Runnable bumpS = () -> { for (int i = 0; i < 100_000; i++) StaticCounter.bump(); };
StaticCounter.reset();
Thread s1 = new Thread(bumpS), s2 = new Thread(bumpS);
s1.start(); s2.start();
s1.join(); s2.join();
System.out.println("StaticCounter = " + StaticCounter.get() + " (200000)");
section("5) Reentrancy: a thread can enter the same monitor twice");
Reentrant r = new Reentrant();
r.outer(); // outer takes the lock, then calls inner which takes it AGAIN
section("6) Two different locks don't contend");
// synchronized(a) and synchronized(b) on different objects run in parallel.
DualCounters dc = new DualCounters();
Thread d1 = new Thread(() -> { for (int i = 0; i < 100_000; i++) dc.bumpA(); });
Thread d2 = new Thread(() -> { for (int i = 0; i < 100_000; i++) dc.bumpB(); });
d1.start(); d2.start();
d1.join(); d2.join();
System.out.println("A = " + dc.a + ", B = " + dc.b);
section("7) Anti-pattern — synchronizing on a pooled object");
// DON'T do this. "hello" is interned; any other piece of code that
// synchronizes on the same literal contends with you.
// synchronized ("hello") { ... } // <- BAD
section("done");
}
// ---------------------------- Helpers ----------------------------
static class Counter {
private int n;
public synchronized void bump() { n++; } // locks `this`
public synchronized int get() { return n; }
}
static class StaticCounter {
private static int n;
public static synchronized void bump() { n++; } // locks StaticCounter.class
public static synchronized int get() { return n; }
public static synchronized void reset() { n = 0; }
}
static class Reentrant {
public synchronized void outer() {
System.out.println("outer acquired " + this);
inner();
}
public synchronized void inner() {
System.out.println("inner re-acquired (reentrant)");
}
}
static class DualCounters {
private final Object lockA = new Object();
private final Object lockB = new Object();
int a, b;
void bumpA() { synchronized (lockA) { a++; } }
void bumpB() { synchronized (lockB) { b++; } }
}
private static void section(String title) {
System.out.println();
System.out.println("=== " + title + " ===");
}
}