線程安全之可見性(三)

一:final的處理

     1.1 經final修飾的變量或者對象,在其構造函數中初始化之後,其他線程一定可以獲得正確的構造版本,即可以獲得變量或者對象字段的最新值。

看下面的代碼:

public class ThreadFinal1 {

    public final int i;
    public int j;

    public ThreadFinal1 final1;

    public ThreadFinal1() {
        this.i = 2; //一定可以得到正確的構造版本
        this.j = 3; //不一定得到正確的構造版本,可能得到默認值0
    }

    public void write() {
        final1 = new ThreadFinal1();
    }

    public void reader() {
        if (final1 != null) {
            System.out.println(final1.i);
            System.out.println(final1.j);
        }
    }
}

       上述代碼,定義了一個final修飾的i,和一個沒用final修飾的j,在構造函數中賦值後,i在其他線程中一定可以獲得正確的構造版本,即i=2,而j不一定可以獲得正確的構造版本,可能獲得j的默認值0。

       1.2 經final修飾的變量或者對象,在其構造函數中初始化之後,其他線程一定可以獲得正確的構造版本,而且字段若重新賦值與final修飾的字段,那麼該字段也一定可以獲得正確的構造版本。

public class ThreadFinal2 {

    public final int i;
    public int j;

    public ThreadFinal1 final1;

    public ThreadFinal2() {
        this.i = 2; //一定可以得到正確的構造版本
        this.j = this.i; //也一定可以得到正確的構造版本
    }

    public void write() {
        final1 = new ThreadFinal1();
    }

    public void reader() {
        if (final1 != null) {
            System.out.println(final1.i);
            System.out.println(final1.j);
        }
    }
}

上述代碼中,j賦值爲i,i是final修飾的字段,那麼j在其他線程中也一定可以獲得正確的構造版本,即j=2。

二:字節分裂(word tearing)

       在有些處理器中,不能對單個的字節進行修改,這樣就會造成一些問題;如字節數組,多個線程想要去修改它,只能通過把該字節數組copy過來修改,然後再把主內存中的字節數組覆蓋掉,這樣會造成只能有一個線程是修改成功的。

       在上圖中,線程1和線程2去修改字節數組,分別修改不同位置的值,這樣就會造成最後只有一個線程修改成功。所以,在多線程編程中,儘量不要去對byte[]數組修改。

三:double和long的特殊處理

       double和long都是64位字節,他們的特殊在於,要去修改值,是分成兩部分進行的,即分成兩個32位字節的,這樣就會導致,出現髒數據,如一個線程修改了值,另一個線程讀取,可能讀到的是隻修改一部分的值。

        所以在多線程編程中,可以用volidate字段去修飾double和long,並不是說volidate修飾的字段是原子性的,只是在double和long中,用volidate修改,可以保證其原子性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章