Try   HackMD
tags: database

Java Concurrency

作業系統學到的同步化知識,Java 內建就有,以下讓我們窺其一二。

使用 Thread

建立執行緒

  1. 實作 Runnable
    ​​​​public class SomeRunnable implements Runnable {
    ​​​​		@Override
    ​​​​		public void run() { /* ... */ }
    ​​​​}
    
  2. 繼承 Thread
    ​​​​public class SomeThread extends Thread {
    ​​​​		@Override
    ​​​​		public void run() { /* ... */ }
    ​​​​}
    
  3. 使用
    ​​​​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 可以使用需要同步的方法。
    ​​​​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 呼叫這倆要同步的方法。
    ​​​​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 真正的範圍。以下就算用上面方法上同步化的物件也沒用。

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 的使用: waitnotify

Java 的所有物件都內建 semaphore 的邏輯!!!

如果某 Thread 得不到需要的物件資源,可以主動呼叫該物件的 Object::wait 方法將執行權讓給其他 Thread;當某 Thread 釋放此物件資源時可以呼叫 Object::notifyAll 方法喚醒「所有」在等待此資源的 Thread,當中的「某一個」物件將會獲得此物件的使用權。注意這裡說「物件的使用權」是自訂義的,Java 並不會限制物件的使用。

另注意,Object::wait 方法總是習慣被包在回圈中,因為只有「某一個」想使用物件的 Thread 可以真的獲得之。