保证系统内存只有一个对象,节省系统资源,对于需要频繁创建销毁的对象,使用单例模式可以提高性能

提供一个获取对象的方法,而不是直接new

单例模式主要分为两大类

  • 饿汉模式:类装载的时候就创建对象
  • 懒汉模式:需要用到对象的时候创建

使用单例模式,首先是要私有化构造器

单例模式各种线程安全和不安全的写法加起来一共有八种写法

防止反射调用

可在私有化的无参构造方法里加一段,调用无参构造直接抛异常

  1. if(instance != null){
  2. throw new RuntimeException("单例模式禁止反射调用");
  3. }

饿汉模式写法

通常使用静态static关键字

  1. public class StaticSingletonTest {
  2. private final static StaticSingletonTest instance = new StaticSingletonTest();
  3. private StaticSingletonTest() {
  4. if(instance != null){
  5. throw new RuntimeException("单例模式禁止反射调用");
  6. }
  7. }
  8. public static StaticSingletonTest getInstance() {
  9. return instance;
  10. }
  11. }

也可以使用静态代码块来写

注意:静态代码块是按照顺序执行的!!!

  1. private final static StaticSingletonTest instance;
  2. static {
  3. instance = new StaticSingletonTest();
  4. }
  5. public static StaticSingletonTest getInstance() {
  6. return instance;
  7. }

两种写法没有本质区别

懒汉模式写法

在需要使用该对象的时候再实例化对象

双重判断同步写法

为了线程安全需要使用synchronized关键字和volatile关键字

这里必须加volalite的原因:

new 对象的过程分为三步:

1.分配空间

2.初始化对象

3.指向对象内存地址。

2和3可能被编译器自动重排,导致判断非空但是实际拿的对象还未完成初始化

  1. public class LazySingleton {
  2. //加volatile关键字避免指令重排
  3. private volatile static LazySingleton instance;
  4. //解决高并发时创建多个对象 最简单的方法就是在方法上加个synchronized,然后方法里只需要做一次判断
  5. //但是这是静态方法锁的就是这个class了,性能受影响,取而代之的是双重判断+同步代码块
  6. //这里只需要锁判断对象是否创建就行了
  7. public static /*synchronized*/ LazySingleton getInstance() {
  8. if (instance == null) {
  9. //优化的方法,使用同步代码块
  10. synchronized (LazySingleton.class) {
  11. //在多线程情况下需要判断,避免多实例
  12. if (instance == null) {
  13. instance = new LazySingleton();
  14. // 1.分配空间 ->返回一个指向该空间的一个内存引用
  15. // 2.对空间进行初始化
  16. // 3.把内存引用复制给 变量
  17. // 但是运行过程中2和3的顺序可能不对,出现指令重排,所以需要volatile关键字定义
  18. }
  19. }
  20. }
  21. return instance;
  22. }
  23. private LazySingleton() {}
  24. }

有线程不安全懒汉式,线程安全懒汉式,同步代码块懒汉式,双重判断懒汉式

这里列举的是双重判断懒汉式

静态内部类写法

  1. public class InnerSingleton {
  2. //外部类在装载的时候静态内部类是不会被装载的
  3. private static class Inner {
  4. private final static InnerSingleton instance = new InnerSingleton();
  5. }
  6. //在此方法调用的时候才装载内部类,而且内部类只装载一次,线程安全
  7. public static InnerSingleton getInstance() {
  8. //调用此属性才会触发内部类的加载
  9. return Inner.instance;
  10. }
  11. private InnerSingleton() {}
  12. }

枚举类的写法

  1. public class EnumForSingleton {
  2. private EnumForSingleton() {}
  3. public EnumForSingleton getInstance() {
  4. return Singleton.INSTANCE.getInstance();
  5. }
  6. private enum Singleton {
  7. /**
  8. * 只写一个属性即可
  9. * 用来获取单实例对象
  10. * 这个枚举类本身也是单例的
  11. */
  12. INSTANCE;
  13. private EnumForSingleton singleton;
  14. /**
  15. * 枚举构造函数创建实例
  16. * JVM这个方法只会调用一次
  17. * 实例调用的时候才初始化
  18. * 属于懒汉模式
  19. */
  20. Singleton() {
  21. singleton = new EnumForSingleton();
  22. }
  23. public EnumForSingleton getInstance() {
  24. return singleton;
  25. }
  26. }
  27. }

单例模式写法虽然有八种,但是能用的只有四种(饿汉的两种写法算一种)