synchronized與單例的線程安全問題

synchronized與單例的線程安全問題

如果對synchronized瞭解不夠深入,建議先理解透徹下面這張圖
來源網絡

遇到的問題

單例中多個方法操作同一變量,出現數據混亂/線程不安全問題。
原先的代碼是synchronized加錯了地方,或者理解不透測。

synchronized

線程同步問題大都使用synchronized解決,有同步代碼塊和同步方法的兩種方式。

代碼示例

錯誤示例一

Single.java

public class Single {

    private final String TAG = "Single";

    private static Single instance = null;

    private int num = 0;

    public static synchronized Single getInstance() {
        synchronized (Single.class) {
            if (instance == null) {
                instance = new Single();
            }
        }
        return instance;
    }


    public void addNum() {
        num++;
        Log.e(TAG, "num:" + num);
    }

    public void reduceNum() {
        num--;
        Log.e(TAG, "num:" + num);
    }

}

MainActivity.java

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }


    public void ClickButton(View view) {

        for (int i = 0; i < 1000; i++) {
            ThreadPool.getInstance().execute(new Runnable() {
                @Override
                public void run() {
                        Single.getInstance().addNum();
                        Single.getInstance().reduceNum();
                }
            });
        }
    }
}

結果:打印num,發現出現很多次num:2的情況。線程不安全。

錯誤示例二

addNumreduceNum方法前加上synchronized

結果:打印num,發現還是會出現num:2的情況。線程不安全。

問題原因及解決

造成上面問題的原因是:即使有線程安全的單例,也僅僅只能保證單例有一個,並不能保證單例中的方法不會出現線程安全問題。因爲當多個線程獲取到該單例對象後,其中的方法就有可能不同步。
錯誤示例二中的方法僅僅只對方法做了同步,並不同保證加減統一。

解決辦法有兩種:
第一種,在線程中加上同步代碼塊。
第二種,在單例代碼中增加一個同步方法,將加減操作放在這個同步方法中。

正確示例一

添加同步代碼塊

Single.java

public class Single {

    private final String TAG = "Single";

    private static Single instance = null;

    private int num = 0;

    public static synchronized Single getInstance() {
        synchronized (Single.class) {
            if (instance == null) {
                instance = new Single();
            }
        }
        return instance;
    }


    public void addNum() {
        num++;
        Log.e(TAG, "num:" + num);
    }

    public void reduceNum() {
        num--;
        Log.e(TAG, "num:" + num);
    }

}

MainActivity.java

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }


    public void ClickButton(View view) {

        for (int i = 0; i < 1000; i++) {
            ThreadPool.getInstance().execute(new Runnable() {
                @Override
                public void run() {
                    synchronized ("") { //添加同步代碼塊
                        Single.getInstance().addNum();
                        Single.getInstance().reduceNum();
                    }
                }
            });
        }
    }
}


正確示例二

增加新的同步方法,將加減方法放在該方法中調用

Single.java

public class Single {

    private final String TAG = "Single";

    private static Single instance = null;

    private int num = 0;

    public static synchronized Single getInstance() {
        synchronized (Single.class) {
            if (instance == null) {
                instance = new Single();
            }
        }
        return instance;
    }

    public synchronized void cal() {
        addNum();
        reduceNum();
    }


    private void addNum() {
        num++;
        Log.e(TAG, "num:" + num);
    }

    private void reduceNum() {
        num--;
        Log.e(TAG, "num:" + num);
    }

}

MainActivity.java

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }


    public void ClickButton(View view) {

        for (int i = 0; i < 1000; i++) {
            ThreadPool.getInstance().execute(new Runnable() {
                @Override
                public void run() {
                    Single.getInstance().cal(); //調用同步方法
                }
            });
        }
    }
}

點我下載Demo

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