单例模式详解(一)

单例模式:

单例模式属于创建型模式:注重完成对象的实例化(框架应用:struts 中的action)

单例模式:确保某个类在系统中仅有一个实例,并提供一个访问它的全局访问点

           jvm 虚拟机中只有一个对象;

           全局访问点:第一次被访问时完成了对象的实例化;

            创建策略:构造函数私有化(其他类不能访问)-----限制单例类实例化

                             定义时含该类的静态私有对象-----创建单例类实例

单例模式写法

public class Singleton {  
  
    //持有私有静态实例,不被引用,null值实现延迟加载  
    private static Singleton instance = null;  
  
    //私有构造方法,防止被实例化 (其他类无法直接访问)  
    private Singleton() {  
    }  
  
    //创建实例 第一次调用该方法时才会哇简称实例化
    public static Singleton getInstance() {  
        if (instance == null) {  
            //实例化
            instance = new Singleton();  
        }  
        //返回引用
        return instance;  
    }  

以上方法线程不安全,多线程情况处理:

1.进程锁

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

   synchronized :锁定对象,每次访问该方法都需要获取进程锁--成本太高,只需要在第一次实例化加锁就可以了。。

2.关键代码同步:


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

  这种代码同步的方法避免了对方法同步,只有在实例化操作时需要加锁,但是线程不安全(如:线程A进入同步代码块,但在实例化单例类前线程被抢占;随后,线程B也进入if 语句,虽然B需要等A完成之前运算,但依然会创建两个不同的单一实例),在Java中创建对象和赋值时分两步(instance = new Singleton()语句)完成的(顺利不定)若只是对A 赋值,B线程也就不会在创建实例,但是返回实例时就会出错。。

3.双重同步锁

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

解决被动初始化的线程安全&&两个线程同时访问getInstance()方法是怎么办?线程A 退出后 线程B会二次检查 看看单例类的实例是否为空,A会设置成员变量值,所有B不会创建第二个单例类的实例了;

4.单例模式用内部类维护单例的实现:

    //内部类
    private static class SingletonFactory{           
            private static Singleton instance = new Singleton();           
        }
     //获取实例            
     public static Singleton getInstance(){           
            return SingletonFactory.instance;           
        }   

JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的(第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,并且保存);

5.可以将创建实例与getInstance() 分开:

private static synchronized void syncInit() {  
        if (instance == null) {  
            instance = new SingletonTest();  
        }  
    }  
    public static SingletonTest getInstance() {  
        if (instance == null) {  
            syncInit();  
        }  
        return instance;  
    }  

写一个方法(加同步),在getInstance() 方法中调用该同步方法即可;

单例模式中的实例化:

被动实例化(懒汉式):调用getInstance() 时实例化 上面几种都是--性能问题;

                    

主动实例化(饿汉式):加载类时实例化单例类,不能延迟加载;

                  

注意:

1.在大型程序中怎么找到单例类呢?----在程序的初始化段创建单例类。。

2.使用单例类,会有其他后果?----------创建单例类子类很难,除非但离妃尚未被实例化。。

3.网上书店的单例类设计在哪?----------目录类,所有用户共享一份书店目录。。

4.使用类的静态方法能实现单例效果吗?--单例类相比静态类更灵活。。

      ---静态类不能实现接口;

      ---单例类可以延迟加载,静态类一般在第一次加载时就初始化了;

      ---单例类可以被继承,方法可以被重写,但静态类的方法都是static的 不能被重写;

单例模式:5种写法(懒汉、饿汉、双重检验锁、静态内部类、枚举)。。。

猜你喜欢

转载自blog.csdn.net/weixin_42249629/article/details/82314278