单例模式(懒汉式和饿汉式)

       在Java中指的是单例设计模式 他是软件开发中最常用的设计模式之一。

单:唯一

例:实例

单例设计模式: 既某个类在整个系统中只能有一个实例对象可被获取和使用的代码模式

要点:

    1.某个类只能有一个实例 

        构造器私有

    2.它必须自行创建这个实例

        含有一个该类的静态变量来保存这个唯一的实例

    3.它必须自行向整个系统提供这个实例

        1.直接暴露 2.用静态变量的get方法来获取


常见的单例形式

饿汉式: (直接创建对象 不存在线程安全问题)

    1.直接实例化饿汉式 (简单直观)

/**

* 饿汉式:

* 在类初始化时直接创建实例对象 不管你是否需要这个对象都会创建 

*

*/

public class Singleton1 {

    public static final Singleton1 INSTANCE=new Singleton1();

    private Singleton1(){} 

}

  2.枚举式 (最简洁) 

/** 

* 饿汉式 枚举类型:表示该类型的对象是有限的几个 

* 我们可以限定一个,就成单例

*/

public enum Singleton2 {

    INSTANCE

}

    3.静态代码块饿汉式 (适合复杂实例化)

/**

* 饿汉式 静态代码块 方式

* 适用于 比较复杂 需要度一堆初始化数据才能将对象创建好的

*/

public class Singleton3 {

    public static final Singleton3 INSTANCE;

    static {

        INSTANCE = new Singleton3();

    } 

    private Singleton3() {


    }


}

创建实例:

public class Test {

    public static void main(String[] args) {

        Singleton1 s = Singleton1.INSTANCE;

        Singleton2 s2 = Singleton2.INSTANCE;

        Singleton3 s3 = Singleton3.INSTANCE;

        System.out.println(s);

        System.out.println(s2);

        System.out.println(s3);

    }

}

饿汉式为什么不存在线程安全问题?

因为:

       类加载的方式是按需加载,且只加载一次。因此,在上述单例类被加载时,就会实例化一个对象并交给自己的引用,供系统使用。换句话说,在线程访问单例对象之前就已经创建好了。再加上,由于一个类在整个生命周期中只会被加载一次,因此该单例类只会创建一个实例,也就是说,线程每次都只能也必定只可以拿到这个唯一的对象。因此就说,饿汉式单例天生就是线程安全的。


懒汉式: (需要时创建对象(延迟创建对象) 存在线程安全问题)

    1.线程不安全 (适用于单线程)

/**

* 懒汉式 延迟创建对象

*  私有构造器用

*  用一个静态变量来保存这个唯一实例

*  提供一个静态方法 获取这个实例

*/

public class Singleton4 {

    private static  Singleton4 instance = null;

    private Singleton4(){}

    public static Singleton4 getInstance(){

        if(instance==null){ 

            instance = new Singleton4();

        }

        return instance;

    }

}

2.线程安全 (适用于多线程)

/**

*   第一种

* 懒汉式 延迟创建对象 (线程安全)

*/

public class Singleton5 {

    private static Singleton5 instance = null;

    private Singleton5(){}

    //在方法上加锁(synchronized) 保证线程安全 在高并发的情况下这种效率比较低 

    public synchronized static Singleton5 getInstance(){

        if(instance==null){

            instance = new Singleton5();

        }

        return instance;

    }

}



/**

* 第二种 DoubleCheck

* 懒汉式 延迟创建对象

*/

public class Singleton6 {

    //加上volatile 保证jvm 不会对代码进行重排序

    private static volatile Singleton6 instance = null;

    private Singleton6(){}

    //保证线程安全 doublecheck

    public  static Singleton6 getInstance(){

        //在线程运行中如果 instance本身不同于空 那样的话就没必要继续进入锁 (效率高于给方法直接上锁)

        if(instance==null){

            synchronized (Singleton6.class){

                if(instance==null){

                    instance = new Singleton6();

                }

            }

        }

        return instance;

    }

}

3.静态内部类形式 (适用于多线程)

/**

* 在内部类被加载和初始话的时, 才创建INSTANCE实例对象

* 静态内部类不会自动随着外部类的加载和初始化而初始化,它是要单独去加载和初始化的

* 因为是在内部类加载时和初始话时才创建对象 因此是线程安全的

*/

public class Singleton7 {

    private Singleton7(){}

    private static class inner{

        private static final Singleton7 INSTANCE=new Singleton7(); 

    }

    public static Singleton7 getInstance(){

        return inner.INSTANCE;

    }

}