单例模式

含义:单例模式,顾名思义就是只有一个实例,并且她自己负责创建自己的对象,这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

 

实现单例模式的四种种方式

 

1、饿汉式:

/**
 * 饿汉式
 * 类加载到内存后,就实例化一个单例,JVM保证线程安全
 * 简单实用,推荐使用!
 * 缺点:不管用到与否,类装载时就完成实例化
 * 相比于枚举单例,不能防止序列化和反序列化攻击
 */
public class MgrSingleton {
    private static final MgrSingleton INSTANCE = new MgrSingleton ();

    private MgrSingleton () {};

    public static MgrSingleton getInstance() {
        return INSTANCE;
    }

    public void m() {
       //业务代码
    }

    public static void main(String[] args) {
        MgrSingleton m1 = MgrSingleton.getInstance();
        MgrSingleton m2 = MgrSingleton.getInstance();
        System.out.println(m1 == m2);
    }
}

2、懒汉式:

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

    private MgrSingleton () {
    }

    public static MgrSingleton getInstance() {
        if (INSTANCE == null) {
            //双重检查
            synchronized (MgrSingleton.class) {
                if(INSTANCE == null) {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    INSTANCE = new MgrSingleton ();
                }
            }
        }
        return INSTANCE;
    }

    public void m() {
        //业务逻辑
    }

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

说明:为什么需要双重判定

synchronized 内if判断的原因,当多个线程来到锁外时,每个线程都会逐步获得锁并且创建新对象,加if避免

synchronized 外if判断的原因,可以不要,但是锁是一个影响效率的操作,使用逻辑判断减少锁操作,增加效率,理论上说,当对象创建完成,外if可以将所有的创建抵挡在外而直接返回

 

3、静态内部类


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

    private MgrSingleton() {
    }

    private static class MgrSingletonHolder {
        private final static MgrSingleton INSTANCE = new MgrSingleton();
    }

    public static MgrSingleton getInstance() {
        return MgrSingletonHolder.INSTANCE;
    }

    public void m() {
        //业务逻辑
    }

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


}

4、枚举类型:

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

    INSTANCE;

    public void m() {
        //业务逻辑
    }

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

}

 

其中枚举单例来自effective java,该方法将会成为最好的单例模式。为什么这样说呢,因为它符合线程安全和延迟加载,并且可以有效防御两种破坏单例(即单例产生多个实例)的行为:反射攻击序列化攻击

例如:

非枚举类型通过反射的方式获取的对象与之前的对象作比较为false

枚举类型是如何做到这一点的

有一篇博客分析的很全面:枚举类型如何避免反射攻击

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值