趣谈java单例模式

单例模式是确保一个类最多只有一个实例,并提供一个全局访问点。就像你是你们家的独生子,一旦邻居跟你爸告状说“你家孩子打架了”,说的只可能是你。
在这里插入图片描述
单例模式有两种创建方式:懒汉式和饿汉式。在下文会对其进行详细解说。

使用场景和优缺点

使用场景

(1)网站计数器。当需要统计当前在线人数,只能用一个全局对象来记录。
(2)应用程序的日志。日志内容一般为共享操作,需要在后面不断写入内容,所以需要单例模式。
(3)线程池或数据库连接池。
(4)操作系统的任务管理器、回收站、文件系统等,都必须确保只有一个。

优缺点

(1)优点:
减少内存开销,尤其是频繁的创建和销毁实例;
避免对资源对过多占用。
(2)缺点:
不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态;
单例类的职责过重,在一定程度上违背了“单一职责原则”。

实现

实现思路

总体来说是:单例类自己给自己创建唯一的实例,如果外面对象需要此实例,它可以提供。实现思路如下:
(1)让构造函数被private修饰,则类外就无法利用new来创建此类的实例了。
我们用形如new Singleton()来创建类的实例,这也就是调用了类的构造函数。如果不想让类外有权限创建此类的实例,用private修饰构造函数即可。

 private Singleton(){}

(2)将获取实例的方法getInstance()用static修饰。
这样,外界想获取类的实例,直接用Singleton.getInstance()方式就可以了。

 public static Singleton getInstance(){};

(3)在getInstance()方法中,系统首先判断是否已经存在实例,如果有了实例,则直接返回此实例即可。否则,创建一个实例,返回结果。

 public static Singleton getInstance()
    {
       if(instance==null){
         instance=new Singleton();
   }
       return instance;
  }

类图

在这里插入图片描述

代码实现

public class Singleton
{
  private static Singleton instance;
  //将构造方法限定为private,避免类在外部被实例化
  private Singleton(){}
  public static Singleton getInstance()
    {
       if(instance==null){
          instance=new Singleton();
   }
       return instance;
  }
}

需要说明一点的是,我们这段代码在第一次被引用时,才会将自己实例化。如果系统不需要这个实例,则永远不会产生。这属于“懒汉式”。

在多线程下的实现

为什么说经典的单例模式是不安全的?

单例模式在多线程环境下是不安全的,因为可能两个线程A、B同时进入getInstance()方法,A线程判断此时instance不存在,打算去创建。还没创建完,cpu资源被线程B夺去,B判断此时instance不存在,也打算去创建。最终,生成了两个instance。

在多线程下应该怎么做?

有3种措施:

  • 使用同步锁(synchronized)

因为synchronized是重量级的,使用会降低性能。所以不推荐使用。
在这里插入图片描述

  • 使用饿汉式(类加载时就将自己实例化)

在这里插入图片描述

  • 使用双重检查加锁

在这里插入图片描述
为什么要加双重检查,而不是单层?

第一层检查的作用:
线程到来后,先判断是否有实例了,如果有了,则不用往下走了。如果没有,再去执行获取锁等接下来的一系列操作。

第二层检查的作用:
我们可以想一个场景,两个线程A B同时进入到第一层的instance==null,然后A获取到锁,去创建实例。等它出来后,B获取到锁,也要去创建实例。如果此时不对“实例是否存在”进行判断,B也会成功创建一个新实例,那此时就不叫“单例模式”了。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卡夫卡的熊kfk

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值