synchronized与单例的线程安全问题

synchronized与单例的线程安全问题

如果对synchronized了解不够深入,建议先理解透彻下面这张图

1.png

遇到的问题

单例中多个方法操作同一变量,出现数据混乱/线程不安全问题。
原先的代码是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(); //调用同步方法
                }
            });
        }
    }
}

复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享