设计模式-单例模式

文章目录

前言

一、什么是单例模式?

二、单例模式的8种写法

1.饿汉式(静态常量)

2.饿汉式(静态代码块)

3.懒汉式(线程不安全)

4.懒汉式(线程安全,同步方法)

5.懒汉式(线程不安全,同步代码块)

6.双重检查

7.静态内部类

8.枚举

总结


前言

设计模式记录学习


一、什么是单例模式?

单例模式是java中最简单的设计模式之一,这种类型的设计模式属于创建类型的一种,它提供了创建对象的最佳方式。

就是采取一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供了一个取得其对象实例的静态方法。

例如Hibernate的sessionFactory 它充当数据存储源代理,并且负责创建Session对象.SessionFactory并不是轻量级,一般情况下,一个项目只需要一个SessionFactory,这时就会用到单例模式

二、单例模式的8种写法

1.饿汉式(静态常量)

优点:   写法简单 类装载的是时候就完成实例化,避免了线程同步的问题
缺点:在类装载的时候完成实例化没,没有达到lazy loading (懒加载)的效果,如果从始至终没有使用过这个实例,则会造成内存浪费

package com.good.app.singleton.type1;

import lombok.Getter;
import org.junit.jupiter.api.Test;

/**
 * 单例模式 1 饿汉式
 *  优点:写法简单 类装载的是时候就完成实例化,避免了线程同步的问题
 *  缺点:在类装载的时候完成实例化没,没有达到lazy loading (懒加载)的效果,如果从始至终没有使用过这个实例,则会造成内存浪费
 */
public class Singleton1 {

    @Getter
    private static final Singleton1 instance = new Singleton1();

    //私有化构造器
    private Singleton1() {
    }


    @Test
    public void test(){
        Singleton1 instance1 = Singleton1.getInstance();
        Singleton1 instance2 = Singleton1.getInstance();
        System.out.println(instance2==instance1);
        System.out.println("instance1.hashCode() = " + instance1.hashCode());
        System.out.println("instance2.hashCode() = " + instance2.hashCode());
    }

}

2.饿汉式(静态代码块)

优点 类装载的是时候就完成实例化,避免了线程同步的问题
缺点 可能存在内存浪费

这种方式和上面的方式其实类似,只不过将类实例化的过程放到了静态代码块种,也是在类装载的时候,就是执行静态代码块种的代码,初始化类的实例

package com.good.app.singleton.type1;

import lombok.Getter;

/**
 * 单例模式2 懒汉式(静态代码块)
 *   优点 类装载的是时候就完成实例化,避免了线程同步的问题
 *   缺点 可能存在内存浪费
 */
public class Singleton2 {

    @Getter
    private static final Singleton2 instance;

    //在静态代码块中 创建单例对象
    static {
        instance = new Singleton2();
    }

    //私有化构造器
    private Singleton2(){
    }

}

3.懒汉式(线程不安全)

提供一个静态的公有方法,当使用到该方法的时候,才去创建实例

优点: 起到了延迟加载的效果,但是只能在单线程下使用

在实际开发种不推荐这种方式

package com.good.app.singleton.type1;

//懒汉式 延迟加载 在使用时候 实例化对象 有线程安全问题
public class Singleton3 {

    private static Singleton3 instance = null;


    private Singleton3() {

    }
    /**
     *  在if 判断 为真 需要new 实例对象的时候有另外的线程进来 也是为真的 这时候就会再次走到 new 实例的 逻辑中
     * 
     */
    public static Singleton3 getInstance() {
        if (instance == null) {
            instance = new Singleton3();
        }
        return instance;
    }
}

4.懒汉式(线程安全,同步方法)

优缺点说明:

优点: 解决了线程不安全的问题

缺点: 效率太低了,每个线程在想得到类的实例的时候,执行方法getInstance()方法都要进行同步,其实这个方法只需要执行一次实例化代码就够了,后面的就直接return 就可以了,方法进行同步效率太低了

package com.good.app.singleton.type1;

//懒汉式 延迟加载 获取实例的时候 在方法体中加入 同步锁 可以保证 每次获取的都是单实例 但是 效率太慢了
public class Singleton4 {

    private static Singleton4 instance = null;


    private Singleton4() {

    }

    /**
     * 加了 同步锁  保证只有一个线程进来 但是 效率变得低下
     */
    public static synchronized Singleton4 getInstance() {
        if (instance == null) {
            instance = new Singleton4();
        }
        return instance;
    }
}

5.懒汉式(线程不安全,同步代码块)

线程还是不安全,执行效率低 不推荐使用

package com.good.app.singleton.type1;

//懒汉式
public class Singleton5 {

    private static Singleton5 instance = null;


    private Singleton5() {

    }

    /**
     * 在创建 实例的时候加同步锁  没有解决多线程的问题 还是会出现多个实例的情况
     */
    public static Singleton5 getInstance() {
        if (instance == null) {
            synchronized (Singleton5.class) {
                instance = new Singleton5();
            }
        }
        return instance;
    }
}

6.双重检查

优点:懒加载 线程安全 推荐使用

package com.good.app.singleton.type1;

//懒汉式  双重锁机制  线程安全 也是懒加载 推荐使用
public class Singleton6 {

    private static volatile Singleton6 instance = null;


    private Singleton6() {

    }

    /**
     * 创建实例之前 在加一个判断  这个时候就不会出现 多个实例被创建的情况 也是满足懒加载的
     */
    public static Singleton6 getInstance() {
        if (instance == null) {
            synchronized (Singleton6.class) {
                if (instance == null) {
                    instance = new Singleton6();
                }
            }
        }
        return instance;
    }
}

7.静态内部类

优缺点说明:

  1. 这种方式采用了类装载的机制来保证初始化实例时只有一个线程。
  2. 静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。
  3. 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
  4. 优点:避免了线程不安全利用静态内部类特点实现延迟加载,效率高
  5. 结论:推荐使用
package com.good.app.singleton.type1;

//延迟加载 静态内部类单例模式 推荐使用
public class Singleton7 {

    private Singleton7() {

    }

    public static class SingletonInstance{
        private static final Singleton7 instance = new Singleton7();
    }

    /**
     * 外部类装载的时候 静态内部类是不会进行装载的 保证懒加载是可以使用的
     * 在调用 getInstance()的时候  获取 静态内部类中的属性 静态内部类会进行装载 jvm类进行装载的时候 线程是安全的
     * 类的静态属性指挥在第一次加载类的时候初始化,
     *
     * @return
     */
    public static Singleton7 getInstance(){
        return SingletonInstance.instance;
    }

}

8.枚举

优缺点说明

  1. 这借助 JDK1.5 中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
  2. 这种方式是 Effective Java作者 JoshBloch 提倡的方式
  3. 结论:推荐使用
package com.good.app.singleton.type1;

//延迟加载 静态内部类单例模式 推荐使用
public enum Singleton8 {

    INSTANCE;

    //方法
    public void method() {
        System.out.println("instance method");
    }


    public static void main(String[] args) {
        Singleton8 instance1 = Singleton8.INSTANCE;
        Singleton8 instance2 = Singleton8.INSTANCE;
        System.out.println("instance1==instance2 = " + (instance1 == instance2));
        System.out.println("instance1.hashCode() = " + instance1.hashCode());
        System.out.println("instance2.hashCode() = " + instance2.hashCode());
        instance1.method();
        instance2.method();
    }

}


单例模式在jdk中的实现 RunTime 类 饿汉式


总结

单例模式注意事项和细节说明

  • 单例模式保证了 系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能

  • 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new

  • 单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session 工厂等)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值