你能看懂的设计模式之单例模式

文章目录


前言

作者目前在复习总结设计模式,便计划写一个设计模式的专题,本篇介绍的是单例模式,主要介绍的是7种单例模式的实现。作者写博客主要是为了归纳总结技术,加深对它的理解,如有任何错误地方请帮忙指出。

单例模式

1.定义

单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。

2.特点

  1. 单例类只有一个实例对象;
  2. 该单例对象必须由单例类自行创建;
  3. 单例类对外提供一个访问该单例的全局访问点。

3.优缺点

优点:

  1. 单例模式可以保证内存里只有一个实例,减少了内存的开销。
  2. 可以避免对资源的多重占用。
  3. 单例模式设置全局访问点,可以优化和共享资源的访问。

缺点:

  1. 单例模式一般没有接口,扩展困难。
  2. 在并发测试中,单例模式不利于代码调试。
  3. 单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。

4.应用场景

  1. 需要频繁创建的一些类,使用单例可以降低系统的内存压力,减少 GC。
  2. 某类只要求生成一个对象的时候,如每个人的身份证号等。
  3. 某些类创建实例时占用资源较多,或实例化耗时较长,且经常使用。
  4. 某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。
    5.当对象需要被共享的场合。

实现方式

1.懒汉模式 (线程不安全)

/**
 * 懒汉模式 (线程不安全)
 * @author winter
 */
public class Singleton_01 {
    private static Singleton_01 instance;
    private Singleton_01() {
    }
    public static Singleton_01 getInstance(){
        if (null != instance) {
            return instance;
        }
        return new Singleton_01();
    }
}

特点:不能再外部创建,也就是new Singleton_01()。
问题:线程不安全,多个访问者同时获取对象实例,就会造成多个同样的实例并存。

2.懒汉模式 (线程安全)

/**
 * 懒汉模式 (线程安全)
 * @author winter
 */
public class Singleton_02 {

    private static Singleton_02 instance;

    private Singleton_02() {
    }

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

}

线程安全,直接将锁加在方法上,所有的访问都会抢锁占用,导致资源浪费,效率低,不推荐。

3.饿汉模式 (线程安全)

/**
 * 饿汉模式 (线程安全)
 * @author winter
 */
public class Singleton_03 {

    private static Singleton_03 instance = new Singleton_03();

    private Singleton_03() {
    }

    public static Singleton_03 getInstance() {
        return instance;
    }

}

在启动时候直接运行加载,后续有外部需要使用时直接获取即可,会造成一些内存浪费问题。

4.使用类的内部类 (线程安全)

/**
 * 使用类的内部类 (线程安全)
 * @author winter
 */
public class Singleton_04 {

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

    private Singleton_04() {
    }

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

}

既保证了线程安全,又保证了懒汉模式,同时不会因为加锁而降低性能。比较推荐的一种单例模式。

5.双重锁校验 (线程安全)

/**
 * 双重锁校验 (线程安全)
 * @author winter
 */
public class Singleton_05 {

    private static Singleton_05 instance;

    private Singleton_05() {
    }

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

}

实现了对方法级锁的优化,减少了获取实例的耗时,也满足了懒汉模式。

6.CAS (线程安全)

/**
 * CAS (线程安全)
 * @author winter
 */
public class Singleton_06 {

    private static final AtomicReference<Singleton_06> INSTANCE = new AtomicReference<Singleton_06>();

    private static Singleton_06 instance;

    private Singleton_06() {
    }

    public static final Singleton_06 getInstance() {
        for (; ; ) {
            Singleton_06 instance = INSTANCE.get();
            if (null != instance) return instance;
            INSTANCE.compareAndSet(null, new Singleton_06());
            return INSTANCE.get();
        }
    }
    
}

在线程竞争不激烈时,相对其他锁的实现,没有线程的切换和阻塞也没有了额外的开销,并且可以支持较大的并发。但在在线程竞争激烈时,CAS会一直自旋。

7.枚举单例 (线程安全)

/**
 * 枚举单例 (线程安全)
 * @author winter
 */
public enum Singleton_07 {
    INSTANCE;
    public void doSomething() {
        System.out.println("doSomething");
    }
    //调用方式
    public static void main(String[] args) {
        Singleton_07.INSTANCE.doSomething();
    }
}

这种方式解决了线程安全、*穿行化和单一实例问题,并且非常简洁,是Joshua.J.Bloch推荐的单例模式。

总结

单例是设计模式中最简单的一种模式,但在各种的实现上却需要用到java的基本功,也是面试中高频率问到一种设计模式,如 1. 请写出一种高效安全的单例模式? 2. 懒汉模式是线程安全吗?为什么?

参考 《重新java设计模式》

上一篇:四,创建者模式 -->单例模式


下一篇:windows—卸载nodejs