设计模式-创建型-单例模式

为什么要使用单例?

单例存在哪些问题?

单例与静态类的区别?

有何替代的解决方案?

 

单例设计模式:一个类只允许创建一个对象(或者实列),那这个类就是单例类,这种设计模式就叫作单例设计模式,简称单例模式

单例的用处

从业务概念上,有些数据在系统中只应保存一份,就比较适合设计单例类。除此之外,我们还可以使用单例解决资源访问冲突问题。

 

如何实现单例模式?

实现单例模式的关注点如下::

  • 构造函数需要private访问权限符修饰,这样才能避免被外部通过new 实列化
  • 需要考虑对象创建的线程安全问题
  • 考虑是否是要延迟加载
  • 考虑getInstance()性能是否高(是否加锁)。

单例模式的实现方式如下

1、饿汉模式

在类加载的时候,instance静态实例就已经创建并初始化好。所以instance是线程安全的。但这种方式不支持延迟加载。

如果实例资源占用多或初始化耗时长,采用饿汉模式虽然会浪费一定的资源,避免程序运行之后,影响程序的性能。如果存在问题,或者资源不够,问题也能及时暴露及时解决。

/**
 * 唯一递增的ID生成器
 *
 * 单例设计模式--饿汉模式
 */
public class IdGeneratorOfHungryMode {
    private AtomicLong id =new AtomicLong(100);
    private static final IdGeneratorOfHungryMode instance=new IdGeneratorOfHungryMode();

    private IdGeneratorOfHungryMode(){}

    public static IdGeneratorOfHungryMode getInstance(){
        return instance;
    }

    public long getId(){
       return id.getAndIncrement();
    }
}

 

2、懒汉模式

懒汉模式相对于饿汉模式来说,就是支持延迟加载。代码实现如下:

/**
 * 唯一递增Id生成器
 *
 * 单例设计模式--懒汉模式
 */
public class IdGeneratorOfLazyMode {
    private AtomicLong id=new AtomicLong(100);
    private static IdGeneratorOfLazyMode instance;

    private IdGeneratorOfLazyMode(){}

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

    public long getid(){
        return id.getAndIncrement();
    }
}

懒汉模式如果考虑线程安全的话,需要给getInstance()方法加锁。如果频繁调用的话,就会存在频繁的加锁、释放锁以及低并发等问题,会存在性能问题。

 

3、双重检测模式

双重检测模式是一种支持延迟加载,有支持高并发的单例设计模式。代码实现如下:

/**
 * 唯一递增id生成器
 *
 * 单例设计模式--双重检测
 */
public class IdGeneratorOfTwoFoldTest {
    private AtomicLong id = new AtomicLong(100);
    private static IdGeneratorOfTwoFoldTest instance;

    private IdGeneratorOfTwoFoldTest(){}

    public IdGeneratorOfTwoFoldTest getInstance(){
        if(instance==null){
            synchronized (IdGeneratorOfTwoFoldTest.class){
                if(instance==null){
                    instance=new IdGeneratorOfTwoFoldTest();
                }
            }
        }
        return instance;
    }

    public long getId(){
        return id.getAndIncrement();
    }
}

这种实现方式中只要instance被创建之后,即使在调用getInstance()方法也不会进入到加锁逻辑当中。这就是它支持高并发的原因

 

4、静态内部类

这是一种及支持延迟加载,又是线程安全的一种实现方式,代码实现如下

/**
 * 唯一递增id生成器
 *
 * 单例模式--静态内部类
 */
public class IdGeneratorOfStaticInnnerClass {
    private AtomicLong id = new AtomicLong(100);
    private IdGeneratorOfStaticInnnerClass(){}

    private static class SingletonHolder{
        private static final IdGeneratorOfStaticInnnerClass instance=new IdGeneratorOfStaticInnnerClass();
    }

    public static IdGeneratorOfStaticInnnerClass getInstance(){
        return SingletonHolder.instance;
    }

    public long getId(){
        return  id.getAndIncrement();
    }
}

SingletonHolder是一个静态内部类,当外部IdGeneratorOfStaticInnerClass被加载的时候,并不会创建SingletonHolder的实例对象。只有当调用getInstance()方法的时候,SingletonHolder才会被加载,这个时候才会创建instance。instance是唯一的,也是线程安全的,都有JVM保证。

 

5、枚举

这是一种通过Java枚举本身特性,来保证线程安全和实例唯一性的一种创建方式。代码实现如下:

/**
 * 唯一递增ID生成器
 *
 * 单例设计模式--枚举
 */
public enum IdGeneratorOfEnum {
    INSTANCE;
    private AtomicLong id=new AtomicLong(100);

    public long getId(){
        return id.getAndIncrement();
    }
}

 

单例模式存在的问题?

  • 对OOP的支持不友好
  • 单例会隐藏依赖关系
  • 单例对代码的扩展性不友好
  • 单例对代码的可测试性不友好
  • 单列不支持有参构造函数

虽然有这么多缺陷,但如果当前需求中有数据在系统中只保存一份,暂时对其又没有扩展需求,同时也不依赖外部系统,那就可以放心大胆的使用单列模式,如果后续需求要求对其进行扩展,再重构这部分代码也不迟。没必要为将来可能不存在的功能买单。

 

单例模式有什么替代解决方案?

保证全局唯一,除了单列模式,还可以使用静态方法来实现。但静态方法并不能解决上面存在的问题。如果要解决这些问题,我们需要从根本上寻找其他方式来实现全局唯一类。比如工厂模式、IOC容器等。还可通过程序员自己来保证(自己在编写代码的时候自己保证自己只创建一个对象)

 

参考:设计模式之美--王争

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值