Loading... # 设计模式(十八)——单例模式 ## 参考 > 大话设计模式  ——  程杰 著 > [单例模式的八种写法比较](https://www.cnblogs.com/zhaoyan001/p/6365064.html) ## 目录 [设计模式(一)——简单工厂模式](https://www.princelei.club/archives/67.html) [设计模式(二)——策略模式](https://www.princelei.club/archives/68.html) [设计模式(三)——设计原则](https://www.princelei.club/archives/116.html) [设计模式(四)——装饰模式](https://www.princelei.club/archives/117.html) [设计模式(五)——代理模式](https://www.princelei.club/archives/119.html) [设计模式(六)——工厂方法模式](https://www.princelei.club/archives/132.html) [设计模式(七)——原型模式](https://www.princelei.club/archives/133.html) [设计模式(八)——模板方法模式](https://www.princelei.club/archives/134.html) [设计模式(九)——外观模式](https://www.princelei.club/archives/135.html) [设计模式(十)——建造者模式](https://www.princelei.club/archives/136.html) [设计模式(十一)——观察者模式](https://www.princelei.club/archives/137.html) [设计模式(十二)——抽象工厂模式](https://www.princelei.club/archives/138.html) [设计模式(十三)——状态模式](https://www.princelei.club/archives/139.html) [设计模式(十四)——适配器模式](https://www.princelei.club/archives/140.html) [设计模式(十五)——备忘录模式](https://www.princelei.club/archives/141.html) [设计模式(十六)——组合模式](https://www.princelei.club/archives/147.html) [设计模式(十七)——迭代器模式](https://www.princelei.club/archives/148.html) [设计模式(十八)——单例模式](https://www.princelei.club/archives/157.html) [设计模式(十九)——桥接模式](https://www.princelei.club/archives/159.html) [设计模式(二十)——命令模式](https://www.princelei.club/archives/160.html) [设计模式(二十一)——职责链模式](https://www.princelei.club/archives/161.html) [设计模式(二十二)——中介者模式](https://www.princelei.club/archives/162.html) [设计模式(二十三)——享元模式](https://www.princelei.club/archives/163.html) [设计模式(二十四)——解释器模式](https://www.princelei.club/archives/173.html) [设计模式(二十五)——访问者模式](https://www.princelei.club/archives/174.html) ## 何为单例模式 - 单例模式(Singleton),保证一个类仅有一个实例,并提供一个访问它的全局访问点。 通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其它实例可以被创建,并且它可以提供一个访问该实例的方法。 ## 实例 1. 饿汉式 ``` /** * 饿汉式 */ public class HungerModel { private static final HungerModel instance = new HungerModel(); private HungerModel() { } public static HungerModel getInstance() { return instance; } } ``` 这种静态初始化的方式是在自己被加载时就将自己实例化,所以形象地称之为饿汉式单例类。 ### 优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。 ### 缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。 2. 懒汉式(双重锁定) ``` /** * 懒汉式(双重锁定) */ public class LazyModel { private static volatile LazyModel instance; private LazyModel() { } public static LazyModel getInstance() { if (instance == null) { synchronized (LazyModel.class) { if (instance == null) { instance = new LazyModel(); } } } return instance; } } ``` 在第一次引用时,才会将自己实例化,所以被称为懒汉式单例类。懒汉式面临着多线程的安全性问题,所以需要双重锁定才可以保证线程安全。 为什么已经在外面判断一个instance不为空了,在synchronized里面还要再判断一遍? 对于instance存在的情况,就直接返回,这没有问题。当instance为null并且同时有两个线程调用getInstance()方法时,它们都可以通过第一重instance==null的判断。然后由于synchronized机制,这两个线程只有一个进入,另一个在外排队等候,必须要其中一个进入并出来后,另一个才能进入。而此时如果没有第二重的instance是否为null的判断,则第一个线程创建了实例,而第二个线程还是可以继续创建新的实例,这样就达不到单例的目的了。 ### 优点:线程安全;延迟加载;效率较高。 3. 静态内部类 ``` /** * 静态内部类 */ public class StaticInnerClass { private StaticInnerClass() { } private static class SingletonInstance { private static final StaticInnerClass instance = new StaticInnerClass(); } public static StaticInnerClass getInstance() { return SingletonInstance.instance; } } ``` 这种方式跟饿汉式方式采用的机制类似,但又有不同。两者都是采用了类装载的机制来保证初始化实例时只有一个线程。不同的地方在饿汉式方式是只要Singleton类被装载就会实例化,没有Lazy-Loading的作用,而静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。 ### 优点:避免了线程不安全,延迟加载,效率高。 4. 枚举类 ``` /** * 枚举 */ public enum Singleton { INSTANCE; public void whateverMethod() { System.out.println("this is a method"); } } ``` ### 优点:借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。 客户端代码 ``` /** * 客户端 */ public class MainClass { public static void main(String[] args) { HungerModel hungerModel1 = HungerModel.getInstance(); HungerModel hungerModel2 = HungerModel.getInstance(); System.out.println(hungerModel1 == hungerModel2); LazyModel lazyModel1 = LazyModel.getInstance(); LazyModel lazyModel2 = LazyModel.getInstance(); System.out.println(lazyModel1 == lazyModel2); Singleton singleton1 = Singleton.INSTANCE; Singleton singleton2 = Singleton.INSTANCE; System.out.println(singleton1 == singleton2); singleton1.whateverMethod(); singleton2.whateverMethod(); } } ``` 输出: ``` true true true true this is a method this is a method ``` Last modification:June 11th, 2020 at 06:22 pm © 允许规范转载
想想你的文章写的特别好https://www.ea55.com/