2019-04-02设计模式-单例设计模式

单例设计模式:我认为是一种思想,只要能保持只有一个对象,那这就是一种单例,来看一个简单的例子

public class Singleton {
    private static Singleton mInstance = new Singleton();

    //需要一个私有构造方法
    //防止外部可以直接 new 对象
    private Singleton() {

    }

    public static Singleton getInstance() {
        return mInstance;
    }
}

上面这也是单例,需要注意的是,构建一个私有的构造方法;但上面的这种写法,会在 Singleton 加载的时候就实例对象,我想在我调用 getInstance 方法时才实例对象,所以出现下面这中写法


public class Singleton {
    private static Singleton mInstance;

    private Singleton() {

    }

    public static Singleton getInstance() {
        if (mInstance == null) {
            mInstance = new Singleton();
        }
        return mInstance;
    }
}

相比上一种,在使用的时候才初始化对象;但是在多线程的请情况下还是可能出现多个 Singleton 对象,所以出现了下面这种写法


public class Singleton {
    private static Singleton mInstance;

    private Singleton() {

    }

    public static synchronized Singleton getInstance() {
        if (mInstance == null) {
            mInstance = new Singleton();
        }
        return mInstance;
    }
}

相比上一种,增加了一个锁判断,解决了多线程问题;但是问题又来了,每次调用 getInstance 都会请求锁,有点消耗性能,所以又出现了下一种写法

public class Singleton {
    private static Singleton mInstance;

    private Singleton() {

    }

    public static Singleton getInstance() {
        if (mInstance == null) {
            synchronized (Singleton.class) {
                //注意,一定要写这个判断,否则就和上一种没有区别
                if (mInstance == null) {
                    mInstance = new Singleton();
                }
            }
        }
        return mInstance;
    }
}

相比上一种,双重校验,减少请求锁的次数;但是,在多线程高并发的情况下还是可能会出现问题,所以增加一个关键字 volatile


public class Singleton {
    //增加关键字 volatile
    private static volatile Singleton mInstance;

    private Singleton() {

    }

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

volatile 关键字的作用主要为
1.保证有序性
2.保持线程的可见性

有序性

我们知道 new 一个对象一共分为三步 1.内存开辟一块空间;2.初始化对象;3.将对象引用指向该内存。但是在多线程的情况下,2、3 两步不一定是循序执行,也可能先执行第三步,在执行第2步;所以多线程的情况下可能会出现 1,2,3;1,3,2同时执行,所以上面代码可能会出现返回的对象,但是对象还没有初始化的情况。加上 volatile 关键字,确保执行顺序,就能有效避免这种情况

线程的可见性

先看一个小小的例子


public class VolatileDemo {
    public static void main(String[] args) {
        VolatileThread thread = new VolatileThread();
        new Thread(thread).start();
        while (true) {
            if (thread.isFlag()) {
                System.out.println("------------------>");
                break;
            }
        }
    }

     static class VolatileThread implements Runnable {
        private boolean flag = false;

        @Override
        public void run() {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            flag = true;
            System.out.println("flag ---->" + isFlag());
        }

        public boolean isFlag() {
            return flag;
        }

        public void setFlag(boolean flag) {
            this.flag = flag;
        }
    }
}

运行上面这段代码的结果是什么呢?是输出下面这样子吗?
flag ---->true
------------------>
然而不是,是输出下面这样子
flag ---->true
为什么 ------------------> 不见了呢?因为在线程中修改 flag 值,相对与 main 线程来说是不可见的。在 flag 上加上 volatile 关键字,就会输出第一中情况。这样字应该比较好理解 volatile 关键字的作用了

然而,上面的写法可能太复杂,所以又有了下面的写法:


public class Singleton {
    private static class Holder {
        private static volatile Singleton mInstance = new Singleton();
    }

    private Singleton() {

    }

    public static Singleton getInstance() {
        return Holder.mInstance;
    }
}

相对于双重校验,简单了不少。

结语

单例设计模式,其实就是一种思想,只要能保证只有一个对象,就能称为单例模式;例如android系统使用的getSystemService(),获取服务,通过静态的HashMap来保存对象,在 static 代码块中初始化,这也是一种单例

你可能感兴趣的:(2019-04-02设计模式-单例设计模式)