Singleton单例模式

概述

单例模式(Singleton Pattern)可以保证系统内存中,使用单例模式的类只有一个对象实例。

代码实现

具体实现一般为:构造方法设为私有,只能通过某个方法得到实例。

1、饿汉式

在类加载到内存时,就实例化了一个单例对象,jvm保证线程安全。

无参构造方法用 private 修饰,防止被 new 实例化。

提供 public 方法 getInstance(),让调用方获取唯一的对象实例。

/**
 * 1、饿汉式
 */
public class Singleton01 {
    private static final Singleton01 instance = new Singleton01();

    private Singleton01() {}

    public static Singleton01 getInstance() {
        return instance;
    }
}

测试用例:

public class Main {
    public static void main(String[] args) {
        // 1、饿汉式
        Singleton01 s1 = Singleton01.getInstance();
        Singleton01 s2 = Singleton01.getInstance();
        System.out.println(s1 == s2); // true
    }
}

这里只需要看输出结果是不是 true 就能得知是否是同一个实例了。

总结:线程安全,效率高,但如果该实例从始至终都没被使用过,则会造成内存浪费,可以使用。

2、懒汉式

对于饿汉式的缺点,懒汉式变成了类加载时不初始化,调用的时候再初始化。

/**
 * 懒汉式
 */
public class Singleton02 {
    private static Singleton02 instance;

    private Singleton02() {}

    public static Singleton02 getInstance() {
        if (instance == null) {
            instance = new Singleton02();
        }
        return instance;
    }
}

虽然上面instance进行了判空才初始化,但是多线程情况下,会有线程安全问题。

测试用例:

public class Main {
    public static void main(String[] args) {
        // 2、懒汉式
        for (int i = 0; i < 10; i++) {
            new Thread(() -> System.out.println(Singleton02.getInstance().hashCode())).start();
        }
    }
}

----------结果--------------
913559464
913559464
913559464
524254826
913559464
913559464
913559464
913559464
2104693287
913559464

同一个对象实例的hash值应该一样,这里经过测试,发现确实会产生不同的对象实例。

总结:线程不安全,不要使用。

3、使用synchronized的懒加载

由于普通的懒加载会有线程安全问题,一般这时候就会想到使用synchronize来解决线程安全问题。

/**
 * 3、使用synchronized的懒加载
 */
public class Singleton03 {
    private static Singleton03 instance;

    private Singleton03() {}

    public static synchronized Singleton03 getInstance() {
        if (instance == null) {
            instance = new Singleton03();
        }
        return instance;
    }
}

对于静态方法getInstance(),我们使用synchronized锁住class,来保证线程安全。

测试用例:

public class Main {
    public static void main(String[] args) {
        // 3、使用synchronized的懒加载
        for (int i = 0; i < 10; i++) {
            new Thread(() -> System.out.println(Singleton03.getInstance().hashCode())).start();
        }
    }
}

----------结果--------------
1030610325
1030610325
1030610325
1030610325
1030610325
1030610325
1030610325
1030610325
1030610325
1030610325

经过测试,发现产生的对象实例都是同一个,大家也可以增加循环次数来验证结果。

总结:线程安全,但是synchronized会使效率降低,不建议使用。

ps:有人会企图减小sychronized锁的范围,来增加效率。

public class Singleton03 {
    private static Singleton03 instance;

    private Singleton03() {}

    public static Singleton03 getInstance() {
        if (instance == null) {
            synchronized (Singleton03.class) {
            	instance = new Singleton03();
            }
         }
        return instance;
    }
}

事实证明这样也会用线程安全问题,多线程环境下完全不能使用。

4、双重检测机制

针对上面的问题,我们可以再多加一次判断来保证线程安全,因为进行了两次判断,所以称为双重检测机制。

/**
 * 4、双重检测机制
 */
public class Singleton04 {
    private static Singleton04 instance;

    private Singleton04() {}

    public static Singleton04 getInstance() {
        if (instance == null) {
            synchronized (Singleton05.class) {
                if (instance == null) {
                    instance = new Singleton04();
                }
            }
        }
        return instance;
    }
}

总结:线程安全,效率高,可以使用。

5、静态内部类

同饿汉式思想差不多,使用jvm保证线程安全,但因为想实现懒汉式的模式,所以构建了静态内部类。

/**
 * 5、静态内部类
 */
public class Singleton05 {

    private Singleton05() {}

    private static class SingletonHolder {
        private static final  Singleton05 instance = new Singleton05();
    }

    public static synchronized Singleton05 getInstance() {
        return SingletonHolder.instance;
    }
}

JVM将推迟 SingletonHolder 的初始化操作,直到开始使用这个类时才初始化,并且由于通过一个静态初始化来初始化 Singleton,因此不需要额外的同步。

总结:线程安全,懒加载,效率高,推荐使用。

6、枚举

枚举,不是很常见的一种写法。很简洁的一种实现方式,提供了序列化机制,保证线程安全,绝对防止多次实例化,即使是在面对复杂的序列化或者反射攻击的时候。

/**
 * 6、枚举
 */
public enum Singleton06 {
    instance;
}

调用的时候直接 Singleton06.instance 即可,非常完美的一种方式。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值