可能你会觉得它是所有设计模式中最简单的一个,我想说的是其实它一点也不简单。还是先解释一下它,所谓单例就是在程序中只会存在一个实例,并提供全局访问的方法。

一、设计需求

a.防止外部实例化多个对象

b.多线程场景下实现同步

c.能提供全局访问被实例化的对象

二、设计方式

1.懒汉式

如下代码,所谓懒汉就是你需要单例的时候才去实例化,在单线程的情况下是没问题的。如果需要满足多线程场景,可以在getInstance() 方法上加个synchronized,由于synchronized采用的是悲观锁策略,性能差的很,不建议这么写。

public class Singleton {
      private Singleton() {}
      private static Singleton single=null;
      public static Singleton getInstance() {
          if (single == null) {
              single = new Singleton();
          }
          return single;
      }
}

2.饿汉式

如下代码,所谓饿汉就是在程序编译器就实例化了。

public class Singleton {
     private Singleton() {}
     private static final Singleton single = new Singleton();
     public static Singleton getInstance() {
          return single;
     }
}

3.双重检查(DoubleCheckLocking-DCL)

public class Singleton {
     private Singleton() {}
     private static Singleton single=null;
     //双重检查,同步锁
     public static Singleton getInstance() {
         if (singleton == null) {
              //在创建对象的时候,是多线程不安全的操作,所以在这里加上同步锁
              synchronized (Singleton.class) {
                  if (singleton == null) {
                      singleton = new Singleton();
                  }
              }
          }
            return singleton;
      }
 }

这种写法不是完全线程安全的,在高并发的场景下可能会存在jvm指令集乱序的问题,需要在single变量前添加volatile(jdk1.5)

4.静态内部类

 public class InnerClassSingleton {
      private InnerClassSingleton() {}
      private static class SingletonHolder {
          private static final InnerClassSingleton instance = new InnerClassSingleton();
      }
      public static InnerClassSingleton getInstance() {
          return SingletonHolder.instance;
      }
  }

5.枚举

public class EnumSingleton {

    private EnumSingleton() {

    }

    public static EnumSingleton getInstance() {
        return Singleton.INSTANCE.getInstance();
    }

    private enum Singleton {
        INSTANCE;

        private EnumSingleton singleton;

        Singleton(){
            singleton = new EnumSingleton();
        }

        public EnumSingleton getInstance(){
            return singleton;
        }
    }
}

编译后的class代码如下:

public class EnumSingleton {
    private EnumSingleton() {
    }

    public static EnumSingleton getInstance() {
        return EnumSingleton.Singleton.INSTANCE.getInstance();
    }

    private static enum Singleton {
        INSTANCE;

        private EnumSingleton singleton = new EnumSingleton();

        private Singleton() {
        }

        public EnumSingleton getInstance() {
            return this.singleton;
        }
    }
}

可以看出枚举是饿汉模式,那枚举是如何保持线程安全的?

三、可能存在的问题

3.1 序列化对单例的破坏(非枚举)

public class Singleton  implements Serializable {

    private final static Singleton INSTANCE  = new Singleton();

    private Singleton(){};

    public static Singleton getInstance(){return INSTANCE;}

    public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
        ObjectOutputStream out = null;
        ObjectInputStream in = null;
        try {
            Singleton singleton1 = Singleton.getInstance();
            File file = new File("/Users/Jobs/traces.txt");
            out = new ObjectOutputStream(new FileOutputStream(file));
            out.writeObject(singleton1);
            out.close();

            in = new ObjectInputStream(new FileInputStream(file));
            Singleton singleton2 = (Singleton) in.readObject();
            in.close();
            System.out.println(singleton1 == singleton2);
        } finally {
            if(out != null) {
                out.close();
            }
            if(in != null) {
                in.close();
            }

        }

    }
}

​ 先将一个文本(file对象)写入创建好的单例对象singleton1,再将这个file对象反序列化成singleton2。简单来说序列化是将对象转为字节流的过程(字节流可以写入磁盘),反序列化就是字节流转成对象的过程。这里需要注意的是writeObject所写入的类必须实现Serializable接口。

©2020 洛易                        皖ICP备15026941号-4 该文件修订时间: 2019-09-12 10:23:08

results matching ""

    No results matching ""