驗證synchronized是否能夠實現原子性(同步)、可見性?

1.問題(爲什麼要實現原子性(同步)與及可見性)?

在高併發開發環境中,如果不控制數據的原子性與及可見性,可能會導致數據的不一致性,也可能導致系統阻塞,嚴重破壞了系統的穩定性。

原子性:即保證數據在同一時段只允許一個線程進行操作

可見性:即保證線程在工作內存中修改完成後,在釋放鎖之前,需要把數據寫回到主內存當中。

2.代碼實現與及驗證


/**
 * 資源類
 */
class MyDataSynchronized {

    //共享變量
    private int number = 1;

    /**
     * 無鎖 寫操作,不保證原子性
     */
    public void notLockWrite() {
        if (notLockRead() == 2) {
            System.out.println(Thread.currentThread().getName() + "\t " + "無需修改!");
            return;
        }
        System.out.println(Thread.currentThread().getName() + "\t " + "修改!");
        this.number = 2;
    }

    /**
     * 無鎖 讀操作,不保證可見性
     */
    public int  notLockRead() {
        return this.number;
    }
}
/**
 * synchronized能夠實現原子性(同步)、可見性
 */
public class SynchronizedDemo {

    public static void main(String[] args) {
        notSync();
    }

    /**
     * 無鎖
     */
    private static void notSync() {
        MyDataSynchronized myDataSynchronized = new MyDataSynchronized();

        //n個線程讀
        for (int i = 1; i < 3; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "\t start!");
                //監測number值是否改變,改變即及時退出
                while (myDataSynchronized.notLockRead() == 1) {
                }
                System.out.println(Thread.currentThread().getName() + "\t end : " + myDataSynchronized.notLockRead());
            }, "Read "+String.valueOf(i)).start();
        }

        //n個線程寫
        for (int i = 1; i < 6; i++) {
            new Thread(() -> {
                //保證讀線程首先執行讀取操作,也保證所有寫線程能同時進行寫操作
                try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}
                myDataSynchronized.notLockWrite();
            }, "Write "+String.valueOf(i)).start();
        }
    }
}

驗證:將共享變量的值進行修改,其中兩個讀取線程,五個寫線程,爲了讀取線程先行運行,開啓寫線程時進行睡眠兩秒,也方便模擬同時寫操作。

預期結果:如果寫線程只能執行成功一次寫操作,讀取線程在寫操作完成後自行結束。

執行結果(線程多次修改,且讀取線程進入阻塞,未能達到預期效果)

原因:讀取線程進入阻塞,原因是修改線程修改的資源未寫回主內存當中,修改的資源對於其它線程是不可見的。

           線程多次修改原因主要是寫線程同時讀到number==1,各自執行寫操作

修正:1.讀取操作加synchronized,寫操作不加。會導致多次修改

           2.寫操作加synchronized,讀取操作不加。會導致讀取線程阻塞

正確修改:在讀與寫操作同時加鎖

    /**
     * 有鎖 寫操作,保證原子性
     */
    public synchronized void useLockWrite() {
        if (useLockRead() == 2) {
            System.out.println(Thread.currentThread().getName() + "\t " + "無需修改!");
            return;
        }
        System.out.println(Thread.currentThread().getName() + "\t " + "修改!");
        this.number = 2;
    }

    /**
     * 有鎖 讀操作,保證可見性
     */
    public synchronized int useLockRead() {
        return this.number;
    }

  執行結果(讀取線程正常完成,寫線程只有一次寫操作)

3.總結

synchronized關鍵字可實現多線程同步與及數據的可見性。

 

如上文章比較簡陋,若其中有不恰當的地方,希望大家一起分享一下自己的意見。共同學習,一起成長。

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