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

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

单例模式(Singleton Pattern)采取一定的方法保证在整个软件系统中, 对某个类只能存在一个对象实例。使用单例模式减少对象的创建,可提升系统的性能,节省系统的资源。

一般来说,实现的方式就是该类只提供一个静态方法获取其对象实例的入口。

单例模式三种实现方式

饿汉式

在类加载的时候, 对象实例就随之创建。

饿汉式实现方式之静态常量式
package com.inconspicuousy.pattern.singleton.hungry;


class Singleton {

    /** 采用静态常量的形式, 在类加载的时候直接进行实例化 */
    private static final Singleton SINGLETON = new Singleton();

    /** 构造器私有化,只能通过静态方法唯一入口实例化对象 */
    private Singleton() {
    }

    /** 直接返回定义的常量 */
    public static Singleton getInstance() {
        return SINGLETON;
    }
}


/**
 * 饿汉式实现方式之静态常量式
 * @author inconspicuousy
 */
public class StaticConstantExample {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance1 = Singleton.getInstance();
        // true
        System.out.println(instance == instance1);
    }
}

饿汉式实现方式之静态代码块
package com.inconspicuousy.pattern.singleton.hungry.block;

class Singleton {

    /** 注意, 这里为什么能够用final修饰, 是因为所有的静态代码都会在类构造器中合成一个方法执行  */
    private static final Singleton singleton;

    /* 通过静态代码块进行初始化 */
    static {
        singleton = new Singleton();
    }

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

    /** 获取对象入口 */
    public static Singleton getInstance() {
        return singleton;
    }
}

/**
 * 饿汉式实现方式之静态代码块
 * @author inconspicuousy
 */
public class BlockCodeExample {

    public static void main(String[] args) {
        // true
        System.out.println(Singleton.getInstance() == Singleton.getInstance());
    }

}
懒汉式的优缺点
优点
  • 实现比较简单,实例随着类的装载而创建,从而避免了线程安全问题。
缺点
  • 在类装载的时候就完成实例化,没有达到懒加载的效果。如果从始至终都没有使用过这个实例,则会造成内存内存的浪费。
懒汉式

只有当实例被使用到时候才会被加载, 使用优先级要高于饿汉式实现方式。

懒汉式实现方式之synchronized关键字同步静态方法
package com.inconspicuousy.pattern.singleton.lazy.sync;

class Singleton {

    /** 创建静态变量 */
    private static Singleton singleton = null;

    /** 构造器私有 */
    private Singleton() {
    }

    /** 利用synchronized关键字保证每次只会有一个线程访问到该类方法 */
    public synchronized static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

/**
 * 利用synchronized关键字实现的懒汉式单例模式
 * @author inconspicuousy
 */
public class SyncExample {
    public static void main(String[] args) {
        // true
        System.out.println(Singleton.getInstance() == Singleton.getInstance());
    }
}

特点
  • 使用synchronized关键字锁住静态方法块虽然达到了线程安全问题,但是每次获取对象都会去竞争锁影响程序的性能。优先考虑双重检查式懒汉式实现方式。
懒汉式实现方式之双重检查
package com.inconspicuousy.pattern.singleton.lazy.check;

import java.util.stream.IntStream;

class Singleton {

    /**
     * 这里采用 volatile 关键字修饰singleton是因为防止某个线程取得是缓存数据创建多个实例
     *
     * 被volatile关键字修饰之后, 线程直接会与内存交互, 对其他线程修改的数据是可见的, 每次线程获取singleton的值时都是直接与内存交互
     * */
    private volatile static Singleton singleton = null;

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

    /**
     * 这里放弃使用synchronized修饰方法的是因为singleton被实例化就会触发一次, 每次都要锁方法影响程序的性能
     *
     * 将synchronized关键字提到只有当singleton为空时才会锁住, 增强程序的性能
     * */
    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                // 这里再次对singleton作判空处理是因为防止某些线程已经读取到singleton为空的情况下, 进入阻塞状态, 解除阻塞后实例化singleton
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

/**
 * 双重检查实现懒汉式单例模式
 * @author inconspicuousy
 */
public class DoubleCheckExample {

    public static void main(String[] args) {

        // 十个线程获取的值一致
        IntStream.rangeClosed(1,10).boxed().forEach(i -> {
            new Thread( () -> {
                System.out.println(Singleton.getInstance().hashCode());
            }).start();
        });
    }
}

懒汉式实现方式之静态内部类
package com.inconspicuousy.pattern.singleton.lazy.inner;

import java.util.stream.IntStream;

class Singleton {

    /** 私有构造器 */
    private Singleton() {
    }

    /** 采用静态内部类的方式, 只有当内部类被加载时实力才会被创建, 实现了懒加载 */
    private static class SingletonInstance {
        private static final Singleton SINGLETON = new Singleton();
        public static Singleton getInstance () {
            return SINGLETON;
        }
    }

    /** 只有调用当前方法时, 静态内部类才会被加载 */
    public static  Singleton getInstance() {
        return SingletonInstance.getInstance();
    }
    
}

/**
 * 采用懒汉式静态内部类实现的单例模式
 * @author inconspicuousy
 */
public class InnerClassExample {
    public static void main(String[] args) {
        // 所有的线程返回值一致
        IntStream.rangeClosed(1,10).boxed().forEach(i -> {
            new Thread(() -> System.out.println(Singleton.getInstance().hashCode())).start();
        });
    }
}
枚举

利用枚举的方式实例化对象

package com.inconspicuousy.pattern.singleton.enumeration;

/**
 * 利用枚举类创建对象
 *
 * 其实枚举的底层创建的实例都是被 static final修饰的
 * 本质上是一种饿汉式的特殊形式
 * */
enum Singleton {
    SINGLETON;
}

/**
 * 利用枚举类实现单例模式
 *
 * @author inconspicuousy
 */
public class EnumExample {
    public static void main(String[] args) {
        // true
        System.out.println(Singleton.SINGLETON == Singleton.SINGLETON);
    }
}

单例模式的使用场景

基于单例模式保证系统内存中该类只会存在一个对象的特点可以用到如下的场景。

  • 需要频繁的进行创建和销毁的对象
  • 创建对象耗时太多或者耗费资源太多但又经常用到的对象

工作中比如:工具类对象、频繁访问数据库或者文件的对象(数据源、session工厂)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值