【单例模式(上)】Java实现单例模式,应该这么学

单例模式

单例模式(Singleton Pattern)是Java中最简单的设计模式之一,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。它可以确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。

单例模式定义及应用场景

单例模式( Singleton Pattern )是指确保一个类在任何情况下绝对只有一个实例,例如,公司CEO,部门经理等。J2EE标准中的ServlerContext,ServletContextConfig等,Spring框架应用中ApplicationContext,数据库的连接池等也是单例形式。

饿汉式单例模式

先来看单例模式的类结构图,如下图示:

饿汉式单例模式在类加载的时候就立即初始化,并且创建单例对象,它绝对线程安全,在线程还没出现以前就实例化,不可能存在访问安全问题。
接下来看饿汉式单例的标准代码:

  private static final GuPaoSingleton GU_PAO_SINGLETON = new GuPaoSingleton();

  private GuPaoSingleton() {}

  public static GuPaoSingleton getInstance() {
        return GU_PAO_SINGLETON;
    }

注:加载顺序 先静态,后动态。先属性,后方法,先上后下。

还有另外一种写法,利用静态代码块的机制:

private static final GuPaoStaticSingleton GU_PAO_STATIC_SINGLETON;

    static {
        GU_PAO_STATIC_SINGLETON = new GuPaoStaticSingleton();
    }

    public static GuPaoStaticSingleton getInstance(){
        return GU_PAO_STATIC_SINGLETON;
    }

这两种写法都非常简单,也非常好理解,饿汉式模式适用于单例对象较少的情况,这样写可以保证绝对线程安全,执行效率高。但是它的缺点也很明显,就是所有对象类加载的时候就实例化。这样一来,系统中就会有大批量的单例对象存在,那么系统初始化就会导致大量的内存浪费,也就是说,不管对象用与不用都占着空间,浪费了内存。

懒汉式单例模式

为了解决饿汉式单例可能带来的内存浪费问题,于是就出现了懒汉式单例的写法,懒汉式单例的特点是,单例对象要在被使用时才会初始化
接下来看懒式单例的标准代码

 private LazySimpleSingleton() {}

 private static LazySimpleSingleton instance = null;

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

但这样写会带来一个问题,如果在多线程环境下,就会出现线程安全问题。
编写线程类 : ExecutorThread

public class ExecutorThread implements Runnable {


    @Override
    public void run() {
        LazySimpleSingleton instance = LazySimpleSingleton.getInstance();
        System.out.println(Thread.currentThread().getName() + instance);

    }
}

客户端测试代码如下:

  public static void main(String[] args) {
        new Thread(new ExecutorThread()).start();
        new Thread(new ExecutorThread()).start();
        System.out.println("End");
    }

测试结果:
在这里插入图片描述
上面代码有一定概率出现两种不同的结果,这就意味着上面的单例存在线程安全隐患。
我们通过不断的切换线程,我们发现在线程环境下LazySimpleSingleton 被实例化了两次,我们得到的可能是两个相同的对象实际上是被后面执行的线程覆盖了
在这里插入图片描述
那么我们如何是的懒汉式单例模式在线程环境下保证安全呢?

来看下面代码,给getInstance()加上synchronized关键字,使这个方法实现同步:

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

通过调试:
在这里插入图片描述
线程安全的问题解决了,但是用synchronized加锁时,在线程数量较多的情况下,如果CPU分配压力上升,则会导致大批线程阻塞。从而导致程序性能下降。
接下来我们看双重检查锁的单例模式:

public class LazyDoubleCheckSingleton {
    
    private LazyDoubleCheckSingleton() {}

    private volatile static LazyDoubleCheckSingleton instance = null;

    public static LazyDoubleCheckSingleton getInstance() {
        //检查是否阻塞
        if (null == instance) {
            synchronized (LazyDoubleCheckSingleton.class) {
                //检查是否需要重新创建实例
                if (null == instance) {
                    instance = new LazyDoubleCheckSingleton();
                    //指令重排序 需要新加 volatile 关键字
                }
            }
        }
        return instance;
    }
}

在这里插入图片描述
通过调试:当第一个线程调用getInstance()方法时,第二个线程也可以调用,当第一个线程执行到synchronized时会上锁,第二个线程就会变成MONTOR状态, 出现阻塞。此时,阻塞并不是基于整个类的阻塞,而是在getInstance()方法内部的阻塞。

但是只要用到synchronized关键字总归要上锁,对程序性能还是存在一定的影响,我们可以从类初始化的角度来考虑,采用静态内部类的方式

采用静态内部类的方式

优点: 兼顾饿汉式单例模式的内存浪费问题和synchronized的性能问题。

public class LazyStaticInnerClassSingleton {
 
    private LazyStaticInnerClassSingleton() { }

    public static final LazyStaticInnerClassSingleton getInstance() {
        return lazyHolder.LAZY_STATIC_INNER_CLASS_SINGLETON;
    }

    private static class lazyHolder {
        private static final LazyStaticInnerClassSingleton LAZY_STATIC_INNER_CLASS_SINGLETON = new LazyStaticInnerClassSingleton();
    }
}

这种方式兼顾饿汉式单例模式的内存浪费问题和synchronized的性能问题。内部类一定是要在方法调用之前初始化,巧妙地避开了线程安全问题。

单例模式破坏

1.反射破坏单例
详细信息
2.序列化破坏单例
详细信息

未完待续。。。
【单例模式(下)】Java实现单例模式,应该这么学

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爱吃薄荷味的口香糖

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

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

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

打赏作者

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

抵扣说明:

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

余额充值