设计模式—(5)单例模式

模式定义

确保一个类只有一个实例,并提供了一个全局访问点。

单例应用场景:有一些对象我们只需要一个,比如:线程池、缓存(cache)、对话框、处理偏好设置和注册表的对象、日志对象、充当打印机、显卡等设备的驱动程序的对象。这些对象只能有一个实例,如果制造出多个实例,就会导致许多问题,比如:程序的行为异常、资源使用过量、或者是不一致的结果。

模式类图

使用一个私有构造器、一个私有静态变量以及一个公有静态函数来实现。

私有构造函数保证了不能通过构造函数来创建对象实例,只能通过公有静态函数返回唯一的私有静态变量。
这里写图片描述

懒汉式-线程不安全

以下实现中,私有静态变量 uniqueInstance 被延迟化实例化,这样做的好处是,如果没有用到该类,那么就不会实例化 uniqueInstance,从而节约资源。

这个实现在多线程环境下是不安全的,如果多个线程能够同时进入if(uniqueInstance == null) ,那么就会多次实例化 uniqueInstance。

public class Singleton {

    private static Singleton uniqueInstance;//私有静态变量

    private Singleton() {}//私有构造器

    public static Singleton getInstance()
    {                      //公有静态方法 
         if(uniqueInstance==null) 
         {
            uniqueInstance==new Singleton();
         }
        return uniqueInstance;
    }
}

懒汉式-线程安全 (第1种方法)

只需要对 getUniqueInstance() 方法加锁,那么在一个时间点只能有一个线程能够进入该方法,从而避免了对 uniqueInstance 进行多次实例化的问题。

//给公有静态方法加同步synchronized
public class Singleton {

    private static Singleton uniqueInstance;//私有静态变量

    private Singleton() {}//私有构造器

    public static synchronized Singleton getInstance()
    {//公有静态方法,加上同步synchronized
        if(uniqueInstance==null) 
        {
            uniqueInstance==new Singleton();
        }
        return uniqueInstance;
    }
}

但是存在一个问题:当一个线程进入该方法之后,无论是否已经实例化,其它线程试图进入该方法都必须等待,如果已经实例化,直接return就好 ,但是同步加在了最外面方法上导致同样会等待,因此性能上有一定的损耗。

饿汉式-线程安全 (第2种方法)

线程不安全问题主要是由于 uniqueInstance 被实例化了多次,如果 uniqueInstance 采用直接实例化的话,就不会被实例化多次,也就不会产生线程不安全问题。但是直接实例化的方式也丢失了延迟实例化带来的节约资源的优势。

public class Singleton{
    private static Singleton uniqueInstance=new Singleton();//保证了线程安全
    private Singleton(){}
    public static Singleton getInstance()
    {
        return uniqueInstance;//已经实例化,直接使用
    }
}

双重校验锁-线程安全(第3种方法)

双重校验锁先判断 uniqueInstance 是否已经被实例化了,如果没有被实例化,那么才对实例化语句进行加锁。否则,不需要同步,直接return。即加锁操作只需要对实例化那部分的代码进行。uniqueInstance 只需要被实例化一次,之后就可以直接使用了。
适用于(java 1.5及之后),早期1.4以及之前版本中,许多jvm对于volatile的实现会导致双重校验锁失效。

public class Singleton {
    //volatile关键字确保:当uniqueInstance被初始化成Singleton实例时,多个线程正确的处理uniqueInstance变量

    private volatile static Singleton uniqueInstance;//私有静态变量
    private Singleton() {}//私有构造器
    public static Singleton getInstance()
    {                                    //公有静态方法
        /*注意:只有第一次才彻底执行下面的代码*/
        if(uniqueInstance==null)  
        {
            synchronized(Singleton.class) 
            {
                if(uniqueInstance==null) 
                {
                    uniqueInstance==new Singleton();
                }
            }
        }
        /*注意:只有第一次才彻底执行上面的代码*/
        return uniqueInstance;
    }
}
为什么 if 双重判断?

考虑下面的实现,也就是只使用了一个 if 语句。在 uniqueInstance == null 的情况下,如果两个线程同时执行 if 语句,那么两个线程就会同时进入 if 语句块内。虽然在 if 语句块内有加锁操作,但是两个线程都会执行uniqueInstance = new Singleton();这条语句,只是早晚的问题,也就是说会进行两次实例化,从而产生了两个实例。因此必须使用双重校验锁,也就是需要使用两个 if 判断。

if (uniqueInstance == null)  
{
    synchronized (Singleton.class) 
     {
        uniqueInstance = new Singleton();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值