vlambda博客
学习文章列表

单例模式-有些对象也要计划生育

单例模式

保证一个类仅而有一个实例,并提供一个访问它的全局访问点。该模式保证产生唯一的实例,这样可以严格地控制客户怎么访问它以及何时访问它。

单例一般结构


 
   
   
 
  1. /**

  2. *

  3. * 饿汉式创建

  4. *

  5. * 创建方式比较简单,

  6. * 类加载时立即实例化对象,可能实例化的对象不被使用,造成内存的浪费

  7. */

  8. public class Singleton1 {


  9. private static Singleton1 instance = new Singleton1();


  10. /**

  11. * 让构造函数为 private,不让其他类可以实例化

  12. */

  13. private Singleton1() {

  14. }


  15. public static Singleton1 getInstance() {

  16. return instance;

  17. }


  18. }

 
   
   
 
  1. /**

  2. *

  3. * 懒汉(饱汉)创建方式

  4. */

  5. public class Singleton2 {


  6. /**

  7. * volatile保存内存可见性

  8. */

  9. private static volatile Singleton2 singleton = null;


  10. private Singleton2() {

  11. }


  12. public static Singleton2 getInstance() {

  13. if (singleton == null) {


  14. //加锁

  15. synchronized (Singleton2.class) {


  16. // 再次判空,不然会有并发问题

  17. if (singleton == null) {

  18. singleton = new Singleton2();

  19. }

  20. }

  21. }

  22. return singleton;

  23. }


  24. }

 
   
   
 
  1. /**

  2. *

  3. * 嵌套类(静态的,非静态的一般称为内部类)创建方式

  4. *

  5. * 只能能访问外部类的静态方法和静态属性

  6. *

  7. */

  8. public class Singleton4 {


  9. private Singleton4() {

  10. }


  11. private static class SingletonHolder {

  12. private static Singleton4 singleton = new Singleton4();

  13. }


  14. public static Singleton4 getInstance(){

  15. return SingletonHolder.singleton;

  16. }


  17. }

 
   
   
 
  1. /**

  2. *

  3. * 枚举创建方式

  4. * 它在类加载的时候会初始化里面的所有的实例,

  5. * 而且 JVM 保证了它们不会再被实例化,所以它天生就是单例的。

  6. *

  7. */

  8. public enum Singleton5 {

  9. /**

  10. * 唯一枚举,

  11. */

  12. INSTANCE;


  13. public void doSomething() {

  14. }

  15. }

像静态初始化这种方式,在自己被加载时就将自己实例化的称为饿汉式单例;而要在第一次被引用时才会将自己实例化的方式称为懒汉式单例。饿汉式它是类一加载就实例化的对象,所以它要提前占有系统资源,而懒汉式面临着多线程访问问题,需要加锁来保证安全性。具体使用哪一种方式,看具体需要。

嵌套类创建方式作为最经典方式,提倡使用。只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance,这样实现了延迟加载,同时又是线程安全,而且不用加锁。