# Synchronization
假設有多個Thread同時使用同一個物件,可能會出現不對的結果/錯誤
像是假設有一個常數i, 同時有多個Thread對其進行遞增50000次
最後可能結果不太對。
```java=
class AnObject {
int value = 0;
}
public class Main {
static final AnObject a = new AnObject();
public static void increment() {
a.value++;
}
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
increment();
}
}).start();
}
new Timer().schedule(new TimerTask() {
@Override
public void run() {
System.out.println("result=" + a.value); //???
}
}, 0, 1000);
}
}
//>
```
## Synchronized Block
在同步區塊中, 只有單一thread能夠執行(不能有多個Thread同時執行), 並且object會被"鎖"住
鎖住某個物件
```java=
synchronized(object) {
//...
}
```
鎖住this
```java=
synchronized(this) {
//...
}
```
鎖住整個class (全局)
```java=
synchronized(xxx.class) {
//...
}
```
因此,
上面的方法我們可以寫成:
```java=
class AnObject {
int value = 0;
}
public class Main {
static final AnObject a = new AnObject();
public static void increment() {
synchronized(a) {
a.value++;
}
}
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
increment();
}
}).start();
}
new Timer().schedule(new TimerTask() {
@Override
public void run() {
System.out.println("result=" + a.value); //1000*1000
}
}, 0, 1000);
}
}
//>
```
這樣也可以
```java=
class AnObject {
int value = 0;
void increment() {
synchronized(this) {
value++;
}
}
}
public class Main {
static final AnObject a = new AnObject();
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
a.increment();
}
}).start();
}
new Timer().schedule(new TimerTask() {
@Override
public void run() {
System.out.println("result=" + a.value); //1000*1000
}
}, 0, 1000);
}
}
```
## Synchronized Method
我們也可以把整個Method "鎖住", 直接加上synchronized修飾詞:
像是上面範例
```java=
public static void increment() {
synchronized(a) {
a.value++;
}
}
//可以寫成
public static synchronized void increment() {
a.value++;
}
```
non static:
```java=
void increment() {
synchronized(this) {
value++;
}
}
//可以寫成
synchronized void increment() {
value++;
}
```
# Volatile
volatile可說是輕量級的synchronized, volatile只能用來修飾變數。
volatile保證了:
- 可見性
保證在更改變數的時候是直接從主內存中更改, 對於任何其他Thread來說, 變化是直接的
- 有序性
保證流程不被指令優化排序
以下範例, 第一個check thread必定能觀測到每次value的改變(撇除while迴圈過慢影響)
```java=
static volatile int value = 0;
public static void main(String[] args) {
new Thread(() -> {
int t = 0;
while (true) {
if (t != value) {
System.out.println("changed to " + value);
t = value;
}
}
}).start();
for (int i = 0; i < 1000; i++) {
try {
Thread.sleep(200); //防止while跑太慢
value++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
new Timer().schedule(new TimerTask() {
@Override
public void run() {
System.out.println("result=" + a.value); //???
}
}, 0, 1000);
}
```
volatile不保證:
- 原子性
原子曾被認為是不可分割的最小單位,
程式也是。原子指的是某個執行步驟能被切割的最小單位。
但volatile這傢伙可不保證原子性
value++其實可分割為:
int t = value; int k = t + 1; value = k;
如下面範例, 最終value不一定是1000*1000
```java=
static volatile int value = 0;
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
value++;
}
}).start();
}
new Timer().schedule(new TimerTask() {
@Override
public void run() {
System.out.println("result=" + value); //???
}
}, 0, 1000);
}
//>
```