Java设计模式之创建型模式-单例模式(Singleton)
2017-07-13 00:12
525 查看
单例模式(Singleton): 保证一个类仅有一个实例,并且提供一个访问它的全局访问点。
提供一个私有静态当前类的对象作为变量
提供一个公有静态方法返回类的实例
以下是使用此方法创建的LazySingleton类实例代码。
这种方式在单线程的环境下可以很好的处理,但是在多线程的环境下,如果多个线程同时访问LazySingleton类,调用getInstance()方法时,多个线程会有可能获得单例类的不同实例从而破坏了单例模式。
为了使上述方法变得线程安全,在多线程的环境下也满足我们的要求,现对类的全局方法进行同步处理,更改后的代码如下。
上述两段代码的差异仅在于getInstance()方法是否有关键字synchronized修饰做同步处理来保证线程安全性。虽然说同步方法保证了线程安全性,但是你会发现我们每次调用一次getInstance()方法时都需要加锁一次,影响效率。
为了避免每次的额外的开销,使用双重锁定,先看下代码的实现。
这里做了两次instance == null的判断,有什么目的?
第一次instance == null的判断可以避免线程每次都进行一次加锁,只是在实例未被创建的时候去进行加锁处理,减少开销。
第二次instance == null的判断是为了防止多个线程同时调用getInstance()方法且都满足第一次instance == null的判断的这种情况,此时若没有第二次instance == null判断,所有线程排队进入锁区执行new LazySingleton()创建新的实例,达不到单例的要求。
以下是使用此方法创建的LazySingleton类实例代码。
饿汉式单例模式相对比较常用,因为它不需要同步,执行的效率也相对较高。但是问题就是在类加载的时候就进行初始化,没有达到懒加载的效果,浪费内存。
现在对如上代码做相应的调整。
在这个方法中,用一个私有内部的静态类来包含我们的单例类实例。当单例类加载的时候,HungrySingletonHolder类不会加载到内存当中,只有当我们调用getInstance()方法的时候,这个静态内部类才会被加载并且创建HungrySingleton类的实例。如果实例化很消耗资源,想让它延迟加载但又不希望在类加载的时候就实例化,那么这种方式就很合适。
单例模式实现的三个基本步骤:
私有构造方法来限制外部类对其直接实例化提供一个私有静态当前类的对象作为变量
提供一个公有静态方法返回类的实例
单例模式实现的几种方法:
懒汉式:
懒汉式单例模式在类创建的时候不创建实例,因此类加载的速度比较快。但是在运行时获取对象需要创建实例,此时速度相对较慢。换句话说,懒汉式单例模式在第一次调用的时候才初始化,这样做避免了内存的浪费。以下是使用此方法创建的LazySingleton类实例代码。
public class LazySingleton { private static LazySingleton instance = null; private LazySingleton() { } public static LazySingleton getInstance() { if (instance == null) { instance = new LazySingleton(); } return instance; } }
这种方式在单线程的环境下可以很好的处理,但是在多线程的环境下,如果多个线程同时访问LazySingleton类,调用getInstance()方法时,多个线程会有可能获得单例类的不同实例从而破坏了单例模式。
为了使上述方法变得线程安全,在多线程的环境下也满足我们的要求,现对类的全局方法进行同步处理,更改后的代码如下。
public class LazySingleton { private static LazySingleton instance = null; private LazySingleton() { } public static synchronized LazySingleton getInstance() { if (instance == null) { instance = new LazySingleton(); } return instance; } }
上述两段代码的差异仅在于getInstance()方法是否有关键字synchronized修饰做同步处理来保证线程安全性。虽然说同步方法保证了线程安全性,但是你会发现我们每次调用一次getInstance()方法时都需要加锁一次,影响效率。
为了避免每次的额外的开销,使用双重锁定,先看下代码的实现。
public class LazySingleton { private static LazySingleton instance = null; private LazySingleton() { } public static LazySingleton getInstance() { if (instance == null) { synchronized (LazySingleton.class) { if (instance == null) { instance = new LazySingleton(); } } } return instance; } }
这里做了两次instance == null的判断,有什么目的?
第一次instance == null的判断可以避免线程每次都进行一次加锁,只是在实例未被创建的时候去进行加锁处理,减少开销。
第二次instance == null的判断是为了防止多个线程同时调用getInstance()方法且都满足第一次instance == null的判断的这种情况,此时若没有第二次instance == null判断,所有线程排队进入锁区执行new LazySingleton()创建新的实例,达不到单例的要求。
饿汉式:
饿汉式单例模式正好与懒汉式单例模式相反,在类加载的时候就创建了实例,完成了初始化,所以类加载比较缓慢,而在运行时获取对象的速度相对较快。以下是使用此方法创建的LazySingleton类实例代码。
public class HungrySingleton { private static HungrySingleton instance = new HungrySingleton(); private HungrySingleton() { } public static HungrySingleton getInstance() { return instance; } }
饿汉式单例模式相对比较常用,因为它不需要同步,执行的效率也相对较高。但是问题就是在类加载的时候就进行初始化,没有达到懒加载的效果,浪费内存。
现在对如上代码做相应的调整。
public class HungrySingleton { private static class HungrySingletonHolder { private static final HungrySingleton INSTANCE = new HungrySingleton(); } private HungrySingleton() { } public static final HungrySingleton getInstance() { return HungrySingletonHolder.INSTANCE; } }
在这个方法中,用一个私有内部的静态类来包含我们的单例类实例。当单例类加载的时候,HungrySingletonHolder类不会加载到内存当中,只有当我们调用getInstance()方法的时候,这个静态内部类才会被加载并且创建HungrySingleton类的实例。如果实例化很消耗资源,想让它延迟加载但又不希望在类加载的时候就实例化,那么这种方式就很合适。
相关文章推荐
- 创建型设计模式---JAVA Singleton模式(单身模式)
- Java设计模式——创建型模式——单例模式(SINGLETON)
- JAVA设计模式(3) —<创建型>单例模式(Singleton)
- JAVA设计模式(03):创建型-单例模式(Singleton)
- 创建型设计模式---JAVA Singleton模式(单身模式)
- Java设计模式(4)——创建型模式之单例模式(Singleton)
- JAVA设计模式(03):创建型-单例模式(Singleton)
- Java经典设计模式-创建型模式-单例模式(Singleton)
- 从Java的角度理解设计模式85: 继续演化:对违反DIP原则的修复和应用创建型模式的展望
- 设计模式单件(Singleton)---对象创建型模式
- Java设计模式:Singleton(单态模式)
- 关于JAVA设计模式中的Singleton
- 面向对象设计模式学习(二):Singleton单件创建型模式
- Java设计模式(二)Prototype(原型),Builder和Singleton(单态)
- 在 Java 中应用设计模式 -- Singleton
- 设计模式杂谈:创建型模式之单件模式(Singleton)
- JAVA设计模式之-Singleton模式
- 在Java中应用设计模式--Singleton
- 在Java中应用设计模式--Singleton
- 在Java中应用设计模式--Singleton