iT邦幫忙

1

[Java] Thread-Safety是什麼 – Part 1

閱讀時間: 10分鐘

Java提供了多線程的執行緒(multithreading),實現多個執行緒(thread)並行執行的技術。
因此JVM可以改善整體處理效能。

雖然多線程的執行緒有強大的好處,但取而代之要應付它衍生的問題-需要實作一個thread-safe的方法,
以確保不同的執行緒(thread)在存取相同的資源時不會出現錯誤的行為或者產生意外。

以下的方法可以讓多線程的執行緒(multithreading)達到thread-safe:

1. Stateless Implementations (無狀態的實作)
在大多報錯情況中,都是有關不正常的執行緒的狀態。如果要做到thread-safe,其中一個方法就要有Stateless Implementations (無狀態的實作)。
為了讓大家更了解什麼是stateless implementations, 會用以下例子詳細講解:

public class MathUtils {
     
    public static BigInteger factorial(int number) {
        BigInteger f = new BigInteger("1");
        for (int i = 2; i <= number; i++) {
            f = f.multiply(BigInteger.valueOf(i));
        }
        return f;
    }
}

factorial() method是一個無狀態決定型function. 只要你輸入一個特定的input就只會輸出一個特定的output。例如你輸入1+1, output就一定是2,而不會是其他數字。
另外,這個method不會受到外在的狀態影響,也不需要刻意去維持某個狀態。所以它是不會受到其他執行緒的影響。
因此,可以說factorial() method是一個thread-safety的其中一個例子。同時也說明stateless implementations可以做到thread-safety。

2. Immutable Implementations
假如需要在不同執行緒之間分享狀態,就需要創建一個不可改變的thread-safe class。
這是一個當被建構後,內部狀態不可被改變的class。
會用以下例子詳細講解:

public class MessageService {
     
    private final String message;
 
    public MessageService(String message) {
        this.message = message;
    }
     
    // standard getter
     
}

大家可以看到物件MessageService 的state是不可改變的,內部沒有其他方式可以把它改變,所以它是一個thread-safe。

3. Thread-Local Fields
在OOP中,物件會透過fields和一個或以上的methods來維持其state。
要做到這樣,我們需要創建一個tread-safe classes令到物件的fields處於thread-local。
只要在thread class中定義private fields就可以做到tread-local,會用以下例子詳細講解:
下面是一個thread class (extends了thread),它儲存了一個integer array。

public class ThreadA extends Thread {
     
    private final List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
     
    @Override
    public void run() {
        numbers.forEach(System.out::println);
    }
}

另一個thread class 就儲存了一個string array

public class ThreadB extends Thread {
     
    private final List<String> letters = Arrays.asList("a", "b", "c", "d", "e", "f");
     
    @Override
    public void run() {
        letters.forEach(System.out::println);
    }
}

兩個classes都有自己既state,不會與其他thread分享自己的state,所以它們都是thread-safe。

除此之外,大家也可以創建一個ThreadLocal instances 作為field。

public class StateHolder {
     
    private final String state;
 
    // standard constructors / getter
}


public class ThreadState {
     
    public static final ThreadLocal<StateHolder> statePerThread = new ThreadLocal<StateHolder>() {
         
        @Override
        protected StateHolder initialValue() {
            return new StateHolder("active");  
        }
    };
 
    public static StateHolder getState() {
        return statePerThread.get();
    }
}

Thread-local fields與一般的class fields是沒有很大分別,只是每個thread需要透過setter/getter的方式來獲得thread-local中的field的copy,所以每個thread都有各自的state。

這一篇先介紹3個方式,還有其他方式會在之後的part 2介紹。


尚未有邦友留言

立即登入留言