五种单例模式的写法

有了一些小成绩就不求上进,这完全不符合我的性格。攀登上一个阶梯,这固然很好,只要还有力气,那就意味着必须再继续前进一步 。

每天进步一点,加油 ! 😄

在这里插入图片描述

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

1、懒汉模式

代码如下:

/**
 * 懒汉模式
 */
public class LHan {

    /**
     * 定义一个私有的静态成员,存放在堆里
     */
    private static LHan instance = null;

    /**
     * 私有构造方法
     */
    private LHan() {
    }

    /**
     * 获取实例
     *
     * @return 返回唯一实例化对象
     */
    public static LHan GetInstance() {
        //判断是否为空,如果为空就重新实例化负责对象
        if (instance == null)
            instance = new LHan();
        return instance;
    }
}

缺点: 线程不安全的

原因: 如果有多个线程调用 GetInstance() 的时候,会创建多个实例

代码如下:

/**
* 获取实例
*
* @return 返回唯一实例化对象
*/
public synchronized static LHan GetInstance() {
   //判断是否为空,如果为空就重新实例化负责对象
    if (instance == null)
          instance = new LHan();
    return instance;
}

缺点: 但不高效

原因: 因为任何时候只能有一个线程调用 GetInstance() 方法,但同步操作是只需要在第一次调用时才被需要,也就是说第一次创建实例对象的时候,也就是说当有多个线程时,同步操作对第 2 个线程始是不起作用的

解决方案: 双重检验锁

说明:

​ 简单来说,就是一种使用同步块加锁的方法,称其为双重检验锁,是因为它有两次检查instance=null ,一次是在同步块外,一次是在同步块内,在同步块内检查就是为了防止多个线程一起进入同步块外的if里,从而造成多个实例的问题。

代码如下:

/**
* 获取实例
*
* @return 返回唯一实例化对象
*/
public static LHan GetInstance() {
   if (instance == null)
        synchronized (LHan.class) {
             //判断是否为空,如果为空就重新实例化负责对象
             if (instance == null) {
                  instance = new LHan();
             }
    }
    return instance;
}

问题仍然存在:instance = new LHan(); 这里存在一个问题,这并非是一个原子操作,实际 JVM 进行了 3 件事也就是给 instance分配内存、调用 LHan 的构造函数来初始化成员变量、将 instance 对象指向分配的内存空间(执行完这步后,instance 就为非 null 了)。但因为 JVM 的即时编译器中存在指令重排序的优化,所以第 2 和第 3 步的执行顺序是不能保证的。当 3 先执行完后,instance 已经是非空了,但没初始化 ,所以第二个线程将会直接返回 instance,然后使用。因此存在多线程问题.

由于单线程中遵守 intra-thread semantics,从而能保证即使 交换顺序后其最终结果不变。但是当在多线程情况下,线程 B 将看到一个还没有被初始化的对象,此时将会出现问题。

解决方案:

1、不允许②和③进行重排序

2、允许②和③进行重排序,但排序之后,不允许其他线程看到。

代码如下:

/**
 * 懒汉模式
 */
public class LHan {

    /**
     * 定义一个私有的静态成员,存放在堆里
     */
    private volatile static LHan instance = null;

    /**
     * 私有构造方法
     */
    private LHan() {
    }

    /**
     * 获取实例
     *
     * @return 返回唯一实例化对象
     */
    public static LHan GetInstance() {
        if (instance == null)
            synchronized (LHan.class) {
                //判断是否为空,如果为空就重新实例化负责对象
                if (instance == null) {
                    instance = new LHan();
                }
            }
        return instance;
    }
}

​ 使用 volatile 修饰 instance之后,之前的 之间的 重排序 将在多线程环境下被禁止,从而保证了线程安全执行。

注意:

​ 这个解决方案需要在 JDK 5 版本之上使用

因为从 JDK 5 开始用 JSR-133 内存模型规范,这个规范增强了 volatile 的语义

2、饿汉模式

代码如下:

/**
 * 饿汉式
 */
public class EHan {

    /**
     * 定义一个私有的静态成员,存放在堆里
     */
    private static final EHan instance = new EHan();

    /**
     * 私有构造方法
     */
    private EHan() {
    }

    /**
     * 获取实例
     *
     * @return 返回一个唯一实例对象
     */
    public static EHan GetInstance() {
        return instance;
    }
}

说明:

​ 单例的实例被声明了 staticfinal 变量 ,在类的第一次加载到内存中时就会初始化,所以创建实例本身是线程安全的。

但这不是 懒加载模式 ,因为会在加载类后就初始化,就算客户端没调用 GetInstance() 方法。

静态内部类:

/**
 * 饿汉式
 */
public class EHan {

    /**
     * 在一个静态类里,定义一个私有的静态成员,存放在堆里
     */
    private static class EHanHolder {
        private static final EHan INSTANCE = new EHan();
    }

    /**
     * 私有构造方法
     */
    private EHan() {
    }

    /**
     * 获取实例
     *
     * @return 返回一个唯一实例对象
     */
    public static EHan GetInstance() {
        return EHanHolder.INSTANCE;
    }
}

枚举:

/**
 * Enum 枚举实现单例模式
 */
public enum EasyEHan {
    INSTANCE;

    public void doSomething() {
        System.out.println("啊哈哈哈");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值