设计模式之单例模式

单例模式
本篇是读《设计模式之禅(第2版)》这本书的笔记,希望大家都读一下这本书,很有意思的一本书。

定义:Ensure a class has only one instance, and provide a global point of access to it.(确保某一个类
只有一个实例,而且自行实例化并向整个系统提供这个实例。)

一、优缺点

1.优点

  • 由于单例模式在内存中只有一个实例,减少了内存开支
  • 减少了系统的性能开销(在Java EE中采用单例模式时需要注意JVM
    垃圾回收机制)
  • 避免对资源的多重占用,例如一个写文件动作,由于只有一个实例存在
    内存中,避免对同一个资源文件的同时写操作
  • 可以在系统设置全局的访问点,优化和共享资源访问

2.缺点

  • 没有接口,扩展很困难
  • 单例模式对测试是不利的。在并行开发环境中,如果单例模式没有完成,是不能进行
    测试的,没有接口也不能使用mock的方式虚拟一个对象
  • 单例模式与单一职责原则有冲突

二、使用场景

Spring中的每个Bean就是一个单例,这样做的优点是Spring容器可以管理这些Bean的生命期

  • 要求生成唯一序列号的环境
  • 在整个项目中需要一个共享访问点或共享数据,例如一个Web页面上的计数器,可以不用把每次刷新都记录到数据库中,使用单例模式保持计数器的值,并确保是线程安全的;
  • 创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源;
  • 需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式(当然,也可以直接声明为static的方式)。

三、注意事项

  • 高并发情况下线程不安全
  • 对象的复制问题,最好不要实现Cloneable接口

四、上代码

1.懒汉式

1.1通过synchronized和 volatile 解决线程安全问题

/**
 * lazy loading
 * 也称懒汉式
 * 虽然达到了按需初始化的目的,但却带来线程不安全的问题
 * 可以通过synchronized解决,但也带来效率下降
 */
public class LazyLoadingDemo1 {
    private static volatile LazyLoadingDemo1 INSTANCE;

    private LazyLoadingDemo1() {
    }

    public static LazyLoadingDemo1 getInstance() {
        if (INSTANCE == null) {
            //双重检查
            synchronized (LazyLoadingDemo1.class) {
                if(INSTANCE == null) {
                    INSTANCE = new LazyLoadingDemo1();
                }
            }
        }
        return INSTANCE;
    }
    
    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
            new Thread(()->{
                System.out.println(LazyLoadingDemo1.getInstance().hashCode());
            }).start();
        }
    }
}

1.2.通过静态内部类方式

/**
 * 静态内部类方式
 * JVM保证单例
 * 加载外部类时不会加载内部类,这样可以实现懒加载
 */
public class LazyLoading {

    private LazyLoading() {
    }

    private static class LazyLoadingHolder {
        private final static LazyLoading INSTANCE = new LazyLoading();
    }

    public static LazyLoading getInstance() {
        return LazyLoadingHolder.INSTANCE;
    }

    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
            new Thread(()->{
                System.out.println(LazyLoading.getInstance().hashCode());
            }).start();
        }
    }
}

2.饿汉式

/**
 * 饿汉式
 * 类加载到内存后,就实例化一个单例,JVM保证线程安全
 * 简单实用,推荐使用!
 * 唯一缺点:不管用到与否,类装载时就完成实例化
 * Class.forName("")
 * (话说你不用的,你装载它干啥)
 */
public class Hungry {
    private static final Hungry INSTANCE = new Hungry();

    private Hungry() {};

    public static Hungry getInstance() {
        return INSTANCE;
    }

    public static void main(String[] args) {
        Hungry hungry1 = Hungry.getInstance();
        Hungry hungry2 = Hungry.getInstance();
        System.out.println(hungry1 == hungry2);
    }
}

3.最优枚举类

/**
 * 不仅可以解决线程同步,还可以防止反序列化。
 */
public enum SingletonDemo {

    INSTANCE;

    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
            new Thread(()->{
                System.out.println(SingletonDemo.INSTANCE.hashCode());
            }).start();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值