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