设计模式------单例模式

     一、 单例模式(Singleton Pattern):

      Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

     二、 单例模式有以下特点:

     1、单例类只能有一个实例。
  2、单例类必须自己创建自己的唯一实例。
  3、单例类必须给所有其他对象提供这一实例。

      一般我们用到的单例模式为:饿汉式和懒汉式。

     三、饿汉式和懒汉式的区别:

        1、从名字上来说,饿汉和懒汉:

         饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,

         而懒汉比较懒,只有当调用getInstance的时候,才会去初始化这个单例。

        2、线程安全:如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

        饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题。

        懒汉式本身是非线程安全的,为了实现线程安全有几种写法,分别是下面的1、在getInstance方法上加同步 ; 2、双重检查锁定 ;3、静态内部类 。

       这三种实现在资源加载和性能方面有些区别:

       第1种,在方法调用上加了同步,虽然线程安全了,但是每次都要同步,会影响性能,毕竟99%的情况下是不需要同步的。

       第2种,在getInstance中做了两次null检查,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,同时避免了每次都同步的性能损耗。

       第3种,利用了class loader(类加载器)的机制来保证初始化instance时只有一个线程,所以也是线程安全的,同时没有性能损耗,所以一般推荐使用这一种。

      3、资源加载和性能:

      饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成,

      而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。

 四、懒汉式单例

public class Singleton {
    /**
     * 懒汉式单例:比较懒,只有调用getInstance的时候,才会去初始化这个单例
     */
    private Singleton(){};
    private static Singleton single = null;
    //静态工厂方法
  public static Singleton getInstance(){
     if (single == null) {
            single = new Singleton();
        }
     return single;
 }
}

       Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。

       但是以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个Singleton实例,要实现线程安全,有以下三种方式,都是对getInstance这个方法改造,保证了懒汉式单例的线程安全。

1、在getInstance方法上加同步

public class Singleton {
    /**
     * 懒汉式单例:比较懒,只有调用getInstance的时候,才会去初始化这个单例
     */
    private Singleton(){};
    private static Singleton single = null;
//   1、在getInstance方法上加同步
    public static synchronized Singleton getInstance(){
        if (single == null) {
            single = new Singleton();
        }
        return single;
    }
}    

2、双重检查锁定(此方式将synchronized关键字移到了getInstance方法里面,同时将singleton对象加上volatile关键字,这种方式既可以避免多线程问题,又不会降低程序的性能。但volatile关键字也有一些性能问题,不建议大量使用)

public class Singleton {
    /**
     * 懒汉式单例:比较懒,只有调用getInstance的时候,才会去初始化这个单例
     */
    private Singleton(){};
    private volatile static  Singleton single = null;
//   2、双重检查锁定
    public static Singleton getInstance(){
        if (single== null) {
            synchronized (Singleton.class){
                if (single == null) {
                    single = new Singleton();
                }
            }
        }
        return single;
    }
}

3、静态内部类

public class Singleton {
    /**
     * 懒汉式单例:比较懒,只有调用getInstance的时候,才会去初始化这个单例
     */
    private Singleton() {};
    // 3、静态内部类
    private static class SingleHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    public static final Singleton getInstance() {
        return SingleHolder.INSTANCE;
    }
}

五、饿汉式单例

 1 public class Singleton {
 2     /**
 3      * 饿汉式单例:在类初始化时,已经自行实例化
 4      */
 5     private static Singleton single = new Singleton();
 6 
 7     private Singleton() {}
 8     //静态工厂方法 
 9     public static Singleton getInstance() {
10         return single;
11     }
12 
13 }

       饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。

六、简单实例

     一个单例类使用的例子,以懒汉式为例,这里为了保证线程安全,使用了静态内部类的方式:

public class TestSingleton {
    
    private String name;
    
    private static TestSingleton single = new TestSingleton();

    private TestSingleton() {}
    //静态工厂方法 
    public static TestSingleton getInstance() {
        return single;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
   public void printInfo(){
       System.out.println("my name is "+ name);
   }
}
public class TMainHolder {
    public static void main(String[] args) {
        TestSingleton ts1 =  TestSingleton.getInstance();
        ts1.setName("李雷");
        TestSingleton ts2 =  TestSingleton.getInstance();
        ts2.setName("韩梅梅"); 
        
        ts1.printInfo();
        ts2.printInfo();
        
        if (ts1 == ts2) {
             System.out.println("创建的是同一个实例");  
        }else{
             System.out.println("未创建的是同一个实例");  
        }
    }
}

运行结果:

      结论:由结果可以得知单例模式为一个面向对象的应用程序提供了对象唯一的访问点,不管它实现何种功能,整个应用程序都会同享一个实例对象。

 参考:https://www.cnblogs.com/kingzzm/p/3321718.html

            https://blog.csdn.net/jason0539/article/details/23297037

猜你喜欢

转载自www.cnblogs.com/zy-l/p/9166370.html