设计模式--单例模式

本文详细介绍了Java中的单例模式,包括饿汉式和懒汉式的实现,以及如何通过枚举解决线程安全和性能问题。饿汉式在类加载时创建单例,而懒汉式则在首次使用时创建。针对懒汉式可能存在的线程不安全和性能问题,文章提出了双重检查锁定的优化方案。最后,枚举的引入提供了一种简洁且线程安全的单例实现方式。
摘要由CSDN通过智能技术生成

目录

1什么是单例模式

2.单例模式的种类

2.1单例模式有两种类型

2.2饿汉式单例模式:

3.懒汉式单例模式优化(线程问题和性能问题)

4.枚举实现

枚举创建单例模式的优势:

5总结


1什么是单例模式

单例模式是指在内存中只会创建一次对象的设计模式,在程序中多次使用同一个对象且作用相同时,为了防止平凡的创建对象使得我们的内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所需的地方共同享用这一个对象.

对象调用分析图:

2.单例模式的种类

2.1懒汉式单例模式

懒汉式单例模式:在真正用到时才去创建对象,懒汉式创建对象的方法是在程序使用对象前,先判断该对象是否已经实例化(判空),若已实例化直接返回该类对象.否则则先执行实例化操作.

流程图:

代码:

/**
 * <pre>
 *懒汉式单例
 * </pre>
 *
 */
public class SingleTon {
    private SingleTon (){}
    private static SingleTon  singleTon;
    private static SingleTon getInstance(){
        if (singleTon==null){
            singleTon=new SingleTon();
        }
        return singleTon;
    }

2.2饿汉式单例模式:

在类加载时就已将创建好对象,等待我们的程序去调用

饿汉式在类加载时已经创建好该对象,在程序调用时直接返回该单例对象即可,即我们在编写代码时就已经指明了要马上创建这个对象,不需要等到被调用时再去创建。

流程图:

 代码:

/**
 * <pre>
 * 饿汉式单例
 * </pre>
 *
 */
public class SingleTon {
    private SingleTon(){}

    private static SingleTon singleTon=new SingleTon();

    public static SingleTon getInstance(){
        return singleTon;
    }

注意:上面的代码已经实例化好了一个SingleTon对象在内存中,不会有多个SingleTon对象实例存在,类在加载时会在堆内存中创建一个SingleTon对象,当类被卸载时,SingleTon对象也随之消亡了.

3.懒汉式单例模式优化(线程问题和性能问题)

问题:经过上面的讲解懒汉式单例已经很好了但是在实际开发中有另一个场景,就是当两个线程同时判断singleTon时都会创建一个singleTon,所以我们需要解决一下线程问题

解决方法:最好的方式就是给我们的核心代码加锁;

//方法一
public static synchronized SingleTon getInstance() {
    if (singleTon == null) {
        singleTon = new SingleTon();
    }
    return singleTon;
}
// 方法二
public static SingleTon getInstance() {
    synchronized{   
        if (singleTon == null) {
            singleTon = new SingleTon();
        }
    }
    return singleTon;
    }

这样就规避了两个线程同时创建SingleTon对象的风险,但是引来另外一个问题:每次去获取对象都需要先获取锁,并发性能非常地差,极端情况下,可能会出现卡顿现象导致我们的性能变差

优化性能:如果没有实例化对象则加锁创建,如果已经实例化了,则不需要加锁,直接获取实例

 代码:

//优化性能
public static SingleTon getInstance() {
    if (singleTon == null) {  // 线程A和线程B同时看到singleTon = null,如果不为null,则直接返回singleTon
        synchronized{ // 线程A或线程B获得该锁进行初始化
            if (singleTon == null) { // 其中一个线程进入该分支,另外一个线程则不会进入该分支
                singleTon = new SingleTon();
            }
        }
    }
    return singleTon;
}

这样就完美解决了线程问题和性能问题了(进行了双重效验)

4.枚举实现

在JDK1.5随着枚举的出现单例模式又有了一个新的实现方式(利用枚举来实现)

代码:

/**
 * <pre>
 * 枚举单例
 * </pre>
 *
 */
public enum SingleTonEnum {
    SINGLETON;
    SingleTonEnum() {
        System.out.println("枚举创建对象了");
    }
    public static void main(String[] args) {
        test();
    }
    public static void test() {
        SingleTonEnum t1 = SingleTonEnum.SINGLETON;
        SingleTonEnum t2 = SingleTonEnum.SINGLETON;
        System.out.print(t1 == t2);
    }
}

控制台:

枚举创建单例模式的优势:

(1)代码对比饿汉式与懒汉式来说,更加地简洁其次,既然是实现单例模式,那这种写法必定满足单例模式的要求,而且使用枚举实现时,没有做任何额外的处理.

(2)它不需要做任何额外的操作去保证对象单一性与线程安全性.

5总结

  (1)单例模式种类:懒汉式和饿汉式

(2)懒汉式:在需要用到对象时才实例化对象

(3)饿汉式:在类加载时已经创建好该单例对象,在获取单例对象时直接返回对象即可,不会存在并发安全和性能问题。

(4)在开发中如果对内存要求非常高,那么使用懒汉式写法,可以在特定时候才创建该对象;

(5)如果对内存要求不高使用饿汉式写法,因为简单不易出错,并且很好解决了并发安全和性能问题

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值