乐趣区

关于java:设计模式之单例模式

什么是单例模式

单例模式属于创立型模式,它提供了一种创建对象的形式,确保只有单个对象被创立。这个设计模式次要目标是想在整个零碎中只能呈现类的一个实例,即 一个类只有一个对象

比拟官网的一段话,能够说解释的很分明了。

解决了什么问题

因为在单例模式中,一个类只有一个对象,从其个性咱们能够很天然的想到单例模式能够
节约空间,节约工夫。

  • 对于频繁应用的对象,尤其是比较复杂的对象创立,节俭了创建对象的工夫。
  • 因为不须要频繁创建对象,咱们的 GC 压力也加重了,而在 GC 中会有 STW(stop the world),从这一方面也节约了 GC 的工夫。

联合单列模式的特点,能够牵强附会的晓得它的利用场景。

实现形式

那么咱们怎么实现一个单例模式呢,咱们来思考咱们的指标 — 保障在一个利用中一个类只有一个对象。

于是,咱们就有下边的步骤:

  • 构造方法私有化
  • 在本类中创立一个对象
  • 定义一个 public 办法,在其余程序应用时提供这个曾经创立好的对象

饿汉式

<font color=’red’>【能够应用】</font>

public class Singleton {private static Singleton instance = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {return instance;}
}
  • 长处
    实现简略,因为在类加载时就实现了实例化对象,没有线程平安问题。
  • 毛病
    因为在类加载时就实例化对象,如果前面咱们没有用到这个对象,就造成了对资源的节约。当然,能够忽略不计。

饿汉式的;另一种写法:
<font color=’red’>【能够应用】</font>

public class Singleton {
    private static Singleton instance = null;

    static {instance = new Singleton();
    }

    private Singleton() {}

    public static Singleton getInstance() {return instance;}
}

懒汉式

也称饱汉式,与饿汉式不同的是,类的实例在取得的办法中创立。
<font color=’red’>【线程不平安,不可应用】</font>

public class Singleton {
    private static Singleton instance = null;

    private Singleton() {}

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

显然,这样写并不保障线程平安。比方有这样两个线程 A,B,它们都去调用 getInstance()办法去拿这个类的对象。因为一开始没有做过实例化,instance 为 null,A 线程在执行完 if (null == instance) 后,这时线程 B 也执行到此处,那 A 线程还没有实现类的实例化,instance 还是 null,故两个线程都调用了 new 去实例化了两个对象。????

那到这里,就会很简略的想到加锁来解决线程平安的问题了。
<font color=’red’>【效率低下,不举荐应用】</font>

public class Singleton {
    private static Singleton instance = null;

    private Singleton() {}

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

察看下面的代码,咱们能够看到,在每次调用 getInstance() 时,即便该类曾经实例化,还是要去做同步,而这种状况只有 return 就行了。

那思考到这里,牵强附会的能想到,既然这样锁在办法下面效率低下,那就放大锁的范畴,所在这个类下面。于是有上面的实现:

<font color=’red’>【线程不平安,不能够应用】</font>

public class Singleton {
    private static Singleton instance = null;

    private Singleton() {}

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

嗯,完满!然而认真看一看,如同哪里不对啊。???? 在多线程场景下,咱们再捋一下这块代码的执行流程。同样有这样两个线程 A,B,它们都去调用 getInstance()办法去拿这个类的对象。因为一开始没有做过实例化,instance 为 null,A 线程在执行完 if (null == instance) 后,拿到了锁。这时线程 B 也执行到此处,然而因为被锁了,就期待 A 线程锁的开释。A 线程执行 new 实例化了 Singleton,执行结束后,开释了锁。那 B 又失去了锁,继续执行 Singleton 的实例化,喜剧!!!两个线程都调用了 new 去实例化了两个对象。????

通过一代又一代人的不懈努力,在此基础上,又有了上面的完满解决形式:

懒汉式双重校验锁

<font color=’red’>【能够应用】</font>

public class Singleton {
    private volatile static Singleton instance = null;

    private Singleton() {}

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

持续下面捋代码的逻辑,等到 A 线程实例化实现后,开释了锁。这时 B 线程执行到if (null == instance),因为 A 线程曾经实现了实例化,故间接返回 Instance。

这里须要留神的是 volatile 关键字,JVM 会有指令重排的状况,会造成获取到的对象没有被实例化。应用该关键字,能够保障润饰的关键字前后执行程序惟一,不会进行指令重排。

外部类

<font color=’red’>【能够应用】</font>

public class Singleton {private Singleton() { }
    
    private static class SingletonHolder{private static Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {return SingletonHolder.instance;}
}

与饿汉式类似,外部类的实现形式都通过在类加载时实例化的形式保障线程平安。

不同点是饿汉式形式是只有 Singleton 类被装载就会实例化,没有 Lazy-Loading 的作用。然而动态外部类的实现形式在 Singleton 类被装载时并不会被立刻实例化,而是在须要实例化时,调用 getInstance 办法,才会装载 SingletonHolder 类,从而实现 Singleton 的实例化。类的动态属性只会在第一次加载类的时候初始化,所以在这里,JVM 帮忙咱们保障了线程的安全性,在类进行初始化时,别的线程是无奈进入的。与饿汉式相比,这种实现形式既没有浪费资源,又能保障线程平安。

枚举

<font color=’red’>【举荐应用】</font>

public enum SingletonEnum {

    instance;

    private SingletonEnum() {}

    public void method() {}
}

class Aa {public static void main(String[] args) {SingletonEnum.instance.method();
    }
}

在最初比拟举荐应用 枚举形式 实现单例模式,IDEA 上新建单例类应用的是第一种饿汉式办法。

欢送拜访集体博客 获取更多常识分享。

退出移动版