快速了解设计模式之单例模式(附赠史上最牛单例)

设计模式多种多样, 今天我们来快速了解其中的单例模式

生活中很多东西都是独一无二的, 比如每个人都是唯一的, 不可能有第二个自己; 电脑中的任务管理器只能打开一个, 那就是单例, 而QQ能打开多个, 那就是多例. 反映在编程里, 单例就是一个类只允许产生一个对象, 不允许创建多个对象!

两个经典的单例模式

说起单例模式, 不得不提到: “饿汉式单例” 和 “懒汉式单例”, 所谓"饿汉"就是程序一运行就迫不及待地产生唯一的对象, 而"懒汉"正好相反, 程序运行起来时不急着创建对象, 直到外界来问它要对象的时候, 才开始创建. 两者各有各的好处, 下面我们一个个分析

饿汉式单例

/**
 * @author cqf
 */
public class Singleton {        //饿汉式单例
    
    //构造器私有,防止外界直接创建本类的对象
    private Singleton() {}
    //产生一个对象
    private static Singleton singleton = new Singleton();
    //对外暴露一个获取实例的静态方法
    public static Singleton getInstance() {
        //返回唯一的实例
        return Singleton.singleton;
    }
}

class SingletonTest {
    
    public static void main(String[] args) {
        Singleton instanceOne = Singleton.getInstance();    //问它要一次对象
        Singleton instanceTwo = Singleton.getInstance();    //问它再要一次对象
        System.out.println(instanceOne==instanceTwo);       //比较两次得到的对象是否是同一个
    }
}

执行结果: 确实是同一个对象
快速了解设计模式之单例模式(附赠史上最牛单例)_第1张图片

懒汉式单例

/**
 * @author cqf
 */
public class Singleton {        //懒汉式单例

    //构造器私有,防止外界直接创建本类的对象
    private Singleton() { }

    //声明一个对象,但不初始化
    private static Singleton singleton = null;

    //对外暴露一个获取实例的静态方法
    public static Singleton getInstance() {
        if (Singleton.singleton == null) {
            singleton = new Singleton();
            return singleton;
        } else {
            return Singleton.singleton;
        }
    }
}

class SingletonTest {

    public static void main(String[] args) {
        Singleton instanceOne = Singleton.getInstance();    //问它要一次对象
        Singleton instanceTwo = Singleton.getInstance();    //问它再要一次对象
        System.out.println(instanceOne == instanceTwo);       //比较两次得到的对象是否是同一个
    }
}

执行结果:
快速了解设计模式之单例模式(附赠史上最牛单例)_第2张图片

两者的比较

快速了解设计模式之单例模式(附赠史上最牛单例)_第3张图片
饿汉式单例在一开始就创建对象, 在程序刚开始运行时可能慢一点, 但是后来再拿对象速度就很快了, Spring容器用的也是这种策略; 因为一开始就创建了, 不确定是否用得到这个对象, 如果用不到的话, 会产生资源浪费, 另外它天生是线程安全的, 因为一开始就创建对象的话, 线程不会误操作. 那么懒汉式的优缺点真好反过来

懒汉式静态内部类单例(史上最牛单例)

有没有办法把两者的优点集中起来, 并抛弃掉缺点(取其精华, 去其糟粕)? 当然有!那就是懒汉式静态内部类单例
话不多说, 上代码:

/**
 * @author cqf
 */
public class Singleton {        //懒汉式静态内部类单例

     //构造器私有,防止外界直接创建本类的对象
     private Singleton() {
         //防止反射破坏单例
         if (InnerSingletonClass.singleton != null) {
            throw new RuntimeException("不允许创建多个实例!");
         }
    }
		
    //静态内部类
    private static final class InnerSingletonClass{
        private static final Singleton singleton = new Singleton();
    }

    //对外暴露一个获取实例的静态方法
    public static  Singleton getInstance() {
        return InnerSingletonClass.singleton;
    }
}

class SingletonTest {

    public static void main(String[] args) {
        Singleton instanceOne = Singleton.getInstance();    //问它要一次对象
        Singleton instanceTwo = Singleton.getInstance();    //问它再要一次对象
        System.out.println(instanceOne == instanceTwo);     //比较两次得到的对象是否是同一个
    }
}

总结

利用静态内部类巧妙地节省了资源又保证了线程安全; 原因是静态内部类默认不加载, 所以节省了资源, 至于线程安全问题, 那是由虚拟机保证的:
虚拟机会保证一个类的clinit()方法在多线程环境中被正确的加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的clinit()方法,其他线程都需要阻塞等待,直到活动线程执行clinit()方法完毕。需要注意的是,其他线程虽然会被阻塞,但如果执行clinit()方法的那条线程退出clinit()方法后,其他线程唤醒后不会再次进入clinit()方法. --《深入理解JVM》

你可能感兴趣的:(快速了解设计模式之单例模式(附赠史上最牛单例))