###### tags: `database` # Java Concurrency 作業系統學到的同步化知識,Java 內建就有,以下讓我們窺其一二。 ## 使用 Thread 建立執行緒 1. 實作 Runnable ```java public class SomeRunnable implements Runnable { @Override public void run() { /* ... */ } } ``` 2. 繼承 Thread ```java public class SomeThread extends Thread { @Override public void run() { /* ... */ } } ``` 3. 使用 ```java var runnableThread = new Thread(new SomeRunnable()); runnableThread.start(); var myThread = new SomeThread(); myThread.start(); ``` 新執行緒的 Function Call 會在新的 Stack 上運作,與當前 Stack 分離。 ## 同步化議題 有別於 Fork Process,Heap 上的記憶體是各個 Thread 共享的(Share Memory),故會有 Race Condition。 Java 提供兩種 Race Condition 的解決方法。 > 以下兩者等價。 > 1. Monitor 風格 對方法同步,也就是一次只能有一個 Thread 可以使用需要同步的方法。 ```java public class MonitorCounter { private int shared_var; public synchronized void set(int val) { shared_var = val; } public synchronized int get() { return shared_var; } } ``` 2. Critical Section 風格 對 Heap Memory 上的物件同步。 因為 Java 的物件都在 Heap 上,故將 `this` 放入就等於一次只能有一個 Thread 呼叫這倆要同步的方法。 ```java public class SyncCounter { private int shared_var; public void set(int val) { synchronized(this) { shared_var = val; } } public synchronized int get() { synchronized(this) { return shared_var; } } } ``` 不過最重要的是,要清楚 Critical Section 真正的範圍。以下就算用上面方法上同步化的物件也沒用。 ```java counter = new SyncCounter(); // shared_var = 0 // Thread 1 int c = counter.get(); ++c; counter.set(c); // Thread 2 int c = counter.get(); --c; counter.set(c); // The result of shared_var can be 0, 1, -1... // because te real critical section is the whole get-and-set process! ``` 解決方法有二,一是使用者把使用者的操作也納入 Critical Section,二是物件提供同步化的 get-and-set 的方法。 ## Semaphore 的使用: `wait` 和 `notify` **Java 的所有物件都內建 semaphore 的邏輯!!!** 如果某 Thread 得不到需要的物件資源,可以主動呼叫該物件的 `Object::wait` 方法將執行權讓給其他 Thread;當某 Thread 釋放此物件資源時可以呼叫 `Object::notifyAll` 方法喚醒「所有」在等待此資源的 Thread,當中的「某一個」物件將會獲得此物件的使用權。注意這裡說「物件的使用權」是自訂義的,Java 並不會限制物件的使用。 另注意,`Object::wait` 方法總是習慣被包在回圈中,因為只有「某一個」想使用物件的 Thread 可以真的獲得之。