你觉得单例对象就那么安全么

单例作为最传统的设计模式,我相信会被最多小伙伴使用

问起单例的特性,小伙伴们肯定会说,保证内存中只有一个单例对象,可以共享。

但是,实际上这只是在单线程中是安全的,如果在多线程中,单例的使用就要格外小心,很多情况会因为数据的操作导致系统崩溃。

 

话不多说,直接上代码

单例代码:
public class SingleTon {

    private static SingleTon instance;

    private static CommonBean commonBean;

    public SingleTon() {
        commonBean = new CommonBean();
        commonBean.setVar1("1");
        commonBean.setVar2("2");
        commonBean.setVar3("3");
    }

    public static SingleTon getInstance(){
        if (instance == null){
            instance = new SingleTon();
        }
        return instance;
    }

    public void doSingleWork(){
        commonBean.doTest();
    }

    public void doSingleWorkWithDataChaneg(){
        commonBean.setVar3("4");
        commonBean.doTest();
    }

}

模拟数据操作对象

public class CommonBean {

    private static final String TAG = "CommonBean";

    private String var1;

    private String var2;

    private String var3;

    private Map<String, String> mMap =null;

    public String getVar1() {
        return var1;
    }

    public void setVar1(String var1) {
        this.var1 = var1;
    }

    public String getVar2() {
        return var2;
    }

    public void setVar2(String var2) {
        this.var2 = var2;
    }

    public String getVar3() {
        return var3;
    }

    public void setVar3(String var3) {
        this.var3 = var3;
    }

    public Map<String, String> getMap() {

        if (mMap == null){
            mMap = new HashMap<>();
        }

        mMap.clear();

        mMap.put("var1",var1);
        mMap.put("var2",var2);
        mMap.put("var3",var3);

        return mMap;
    }

    public void doTest(){
        for (String s:getMap().values()
             ) {
            Log.i(TAG, "doTest: "+s);
            try {
                if (s.equals("3")) {
                    Thread.sleep(5 * 1000);
                    Log.i(TAG, Thread.currentThread().getName()+"开始等待...");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public String toString() {
        return "CommonBean{" +
                "var1='" + var1 + '\'' +
                ", var2='" + var2 + '\'' +
                ", var3='" + var3 + '\'' +
                '}';
    }

 

调用方式很简单,新建两个线程,记住不要起匿名线程,这样不利于追踪

TestThread thread1 = new TestThread(test1,"test1");
TestThread thread2 = new TestThread(test2,"test2");

thread1.start();
thread2.start();

 

private class TestThread extends Thread{
    public TestThread(Runnable target, String name) {
        super(target, name);
    }

    @Override
    public void run() {
        super.run();
        Log.i(TAG, "run: " + currentThread().getName());
    }
}


Runnable test1 = new Runnable() {
    @Override
    public void run() {
        SingleTon.getInstance().doSingleWork();
    }
};

Runnable test2 = new Runnable() {
    @Override
    public void run() {
        SingleTon.getInstance().doSingleWorkWithDataChaneg();
    }
};

 

第一个起来的线程会在进入的运行的时候等待5s,而第二个线程立马进入会立即调用数据修改,便会导致出错,map对象在foreach循环里不允许对象修改

其实这个时候多线程对单例的操作,可以被认为是生成了不同的对象,所以这个时候的单例,就不是那么安全了

 

解决方法也很简单,在对数据操作部分加上方法锁

public synchronized void doTest()

如果是Android还可以使用单线程池

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值