---
# System prepended metadata

title: Synchorization/Volatile/Atomics
tags: [Java]

---

# 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 + １; 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);
    }
//>
```