设计模式: 单例模式

单例模式时指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点,在我们生活中有很多例子,比如一个公司只有一个CEO。
下面来看下单例模式的几种实现:

饿汉式单例:
饿汉式单例模式在类加载时就创建好对象,等着调用者来获取这个对象
优点:绝对的线程安全,因为没有加锁,执行效率高
缺点:不管这个对象有没有被用到都会占着内存,浪费资源

public class SingletonHungry {

    public static SingletonHungry SINGLETONHungry = new SingletonHungry();

    private SingletonHungry() {
    }

    public static SingletonHungry getInstance() {
        return SINGLETONHungry;
    }
}

懒汉式单例:
优点:实现了懒加载,适合在单线程下使用
缺点:多线程下会存在线程不安全的问题

public class Singleton1 {

    public static Singleton1 singleton1 = null;

    private Singleton1() {
    }

    public static Singleton1 getInstance() {
        if (singleton1 == null) {
            singleton1 = new Singleton1();
        }
        return singleton1;
    }
}

根据上面这个懒汉式单例我们可以做优化:

public class Singleton2 {

    public static Singleton2 singleton1 = null;

    private Singleton2() {
    }

    public static synchronized Singleton2 getInstance() {
        if (singleton1 == null) {
            singleton1 = new Singleton2();
        }
        return singleton1;
    }
}

上面Singleton2中给创建对象方法加入了同步锁,这样可以保证线程安全,但是效率低,我们还可以这样:

public class Singleton3 {

    public static Singleton3 singleton3 = null;

    private Singleton3() {
    }

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

在代码块中加入同步锁,这样会比在方法上加同步锁性能会高一些,同时也保证了对象实例只能创建一次,我们来模仿多线程情况测试一下:

public class Singleton3Test {

    public static void main(String[] args) throws Exception {

        Thread thread1 = new Thread(
                new Runnable() {
                    public void run() {
                        Singleton3 singleton3 = Singleton3.getInstance();
                        System.out.println("Singleton3的hashCode:" + singleton3.hashCode());
                    }
                });

        Thread thread2 = new Thread(
                new Runnable() {
                    public void run() {
                        Singleton3 singleton3 = Singleton3.getInstance();
                        System.out.println("Singleton3的hashCode:" + singleton3.hashCode());
                    }
                });

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

测试方法运行结果:
Singleton3的hashCode:407939276
Singleton3的hashCode:1224672400
代码块中加同步锁结果还是没有保证线程的安全,下面我们对它再来改进一下:

public class Singleton4 {

    public static Singleton4 singleton4 = null;

    private Singleton4() {
    }

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

这次的运行结果:
Singleton4的hashCode:802393921
Singleton4的hashCode:802393921
可以看到这次保证了线程的安全,这种单例模式叫双重锁判断,其实还有一种方式可以实现懒汉式单例,那就是使用内部类加载机制,内部类只有在被调用的时候才会加载,利用JVM类加载时的线程安全特征来保证对象只加载一次

public class Singleton {

    private Singleton() {
    }

    public static class a {
        public static Singleton SINGLETON = new Singleton();
    }

    public static Singleton getInstance() {
        return a.SINGLETON;
    }
}

上面可以实现一个对象只能被创建一次,但是JAVA中还有很多方式可以创建新的对象,比如反射、反序列化、克隆下面我们需要防止这些操作再生成一个同样的对象

public class Singleton implements Serializable {

    private static final long serialVersionUID = -7386993593450962036L;

    public static Singleton singleton = null;

    private Singleton() {
        // 防止反射破坏单例
        if (singleton != null) {
            throw new RuntimeException("Singleton 只能被创建一次");
        }
    }

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

    /**
     * 防止反序列化破坏单例
     *
     * @return Singleton
     */
    private Object readResolve() {
        return singleton;
    }

    /**
     * 防止克隆破坏单例
     *
     * @return
     */
    @Override
    public Singleton clone() {
        return singleton;
    }
}

这样就是一个比较完美的单例模式了,保证了线程安全,实现了懒加载,防止反序列化、克隆、反射来破坏单例。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容