设计模式——单例模式

简介

Java中设计模式主要分为三种,创建型模式,结构型模式,行为型模式
单例模式则属于创建型模式,它提供了一种创建对象的方式
顾名思义就是一个类只有单个对象被创建,并且外部类可以直接去访问该对象,而不需要再去实例化,很像类中的静态方法直接调用即可

总结:
单例类只能由自己创建自己的唯一实例,并且给其他对象提供这一实例

优点:减少频繁创建对象浪费资源
缺点:无法像直接去创建对象那样灵活

示例:

public class Single {
  // 创建对象
  private static Single single;
  //让构造函数为 private,这样该类就不会被实例化,也就是无法被new(如果不声明private,Java会自定义一个无参的public构造函数)
  private Single(){}
  // 获取唯一可用的对象
  public static Single getInstance() {
    return single;
  }
}

让我们在另一个类中测试一下

public class Demo {

  public static void main(String[] args) {
    Single instance = Single.getInstance();
    instance.sayHello();
  }
}

这样我们不论在多少个类中去使用Single中的sayHello方法,其实都是用的同一个对象,这样可以减少频繁的创建和销毁对象所浪费的资源

接下来让我们看一下单例模式的几种实现方式

1.懒汉式,线程不安全

public class Single {
  private static Single single;

  private Single(){}

  public static Single getInstance() {
    if (single == null) {
      single = new Single();
    }
    return single;
  }
}

根据代码,我们可以看到它在创建对象实例的时候去做了判断,如果没有对象则去创建,如果已有则直接使用。
但问题显而易见,在多线程环境下,假如两个线程同时进入到这个方法,那么可能就会创建出多个实例对象,所以该方法在多线程环境下可能会有问题

2.懒汉式,线程安全

public class Single {
  private static Single single;

  private Single(){}

  public static synchronized Single getInstance() {
    if (single == null) {
      single = new Single();
    }
    return single;
  }
}

这种方式与第一种方式唯一的不同就是加了锁,在获取对象的时候锁住,这样就使得两个线程没办法同时进入这个方法,保证了线程安全问题,但缺点也很明显,每次使用该对象的时候都去加锁判断,会对效率产生一定的影响

3.饿汉式,线程安全

public class Single {
  private static Single single = new Single();

  private Single(){}

  public static Single getInstance() {
    return single;
  }
}

在类加载时就去实例化,相比第一种避免了线程安全问题,相比第二种提升了效率,但可能会浪费一定的资源

4.双检锁

public class Single {
  private volatile static Single single;

  private Single() {
  }

  public static Single getInstance() {
    if (single == null) {
      synchronized (Single.class) {
        if (single == null) {
          single = new Single();
        }
      }
    }
    return single;
  }
}

这种方式主要是为了解决第二种方式中的效率问题,第二种方式中我们将整个getInstance锁住了,但其实我们的主要目的就是避免重复创建对象

所以我们可以将判断对象是否存在拿到锁的外面,也就是第一个if (single == null),这一步主要是解决假设该对象实例已经存在,那么就没必要每次都去加锁判断了,而是直接判断就行

如果这里为true,证明对象不存在,需要去新建,这里有了个synchronized 锁,这里的锁的作用其实是和方式二中的锁一样的,去避免多线程情况下重复创建对象

5.静态内部类

public class Single {
  private static class SingletHolder {
    private static final Single SINGLE = new Single();
  }

  private Single() {}

  public static Single getInstance() {
    return SingletHolder.SINGLE;
  }
}

这种方式其实像是第三种方式的优化,方式三中将实例化对象设为静态,在类加载的时候调用,会造成一定程度的资源浪费
但这种方式在加载类的时候不会去调用,因为静态内部类的优点在于,外部类加载时不会立即加载内部类,所以不会去实例化对象,所以当Single类被加载时,不会去加载SingletHolder类,当getInstance()方法被调用时才会去加载,这样就保证了资源的高效利用,至于内部类如何保证了线程安全问题,这里其实是由虚拟机保证的,虚拟机会保证如果有多个线程去初始化同一个类并执行某个方法时,只会有一个线程去执行,其他线程都会阻塞

总结

饿汉式:优点在于适用于多线程,调用速度快,因为一开始就创建好了对象,缺点是浪费资源
懒汉式:多线程情况下需要通过双检锁等手段处理,性能会降低,调用时初始化时间长
其实是正好相反的两种模式,看需要具体使用哪个

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值