单例模式的几种实现方法

单例模式

单例模式是指在内存中只会创建且仅创建一次对象的设计模式。在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可从让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。

单例的模式好处可以既可以提升效率,又可以让类自己把控实例的实现细节,对外部业务代码不产生任何影响。

各种单例模式的实现代码

懒汉模式(线程不安全的)

package com.kikhot.design.singleton;

/**
 * 懒汉模式-线程不安全
 */
public class UnSafeLazySingleton {
    // 对象并没有初始化
    private static UnSafeLazySingleton instance;

    private UnSafeLazySingleton() {
    }


    // 此处没有加synchronized所有不是同步的,多线程会产生问题,导致多个实例
    public static UnSafeLazySingleton getInstance() {
        if (instance == null) {
            instance = new UnSafeLazySingleton();
        }
        return instance;
    }

}

上述之所以被称为懒汉模式,主要是指 instance 在需要的时候才会被创建对象实例。

这里并没有保证线程安全问题,即当多个线程同时访问 getInstance() 方法时,会出现多个线程同时对 instance 进行对象创建的情况。

懒汉模式(线程安全)

package com.kikhot.design.singleton;

/**
 * 懒汉模式-线程安全
 */
public class SafeLazySingleton {

    private static SafeLazySingleton instance;

    private SafeLazySingleton() {
    }

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

}

在静态方法上加入 sychronized 关键字,可以保证多个线程在初始化单例模式的时候,只有一个线程介入。从而可以保证是一个线程安全的模式。

饿汉模式(线程安全)

package com.kikhot.design.singleton;


/**
 * 饿汉模式-线程安全
 * 类加载时就初始化,但是会出现浪费内存的情况
 * 基于 classloader 机制避免了多线程的同步问题, instance 在类加载时就进行实例化了
 */
public class HungrySafeSingleton {

    private static HungrySafeSingleton instance = new HungrySafeSingleton();

    private HungrySafeSingleton() {
    }

    public static HungrySafeSingleton getInstance() {
        return instance;
    }

}

在类加载的时候就进行了一个 classloader 机制的实例化创建。从而避免了线程不安全的情况。

双重双检锁

package com.kikhot.design.singleton;

/**
 * 饿汉模式-线程安全-双重双检锁
 */
public class DoubleCheckSingleton {

    // 基于volatile来实现内存可见
    private volatile static DoubleCheckSingleton instance;

    private DoubleCheckSingleton() {
    }

    public static DoubleCheckSingleton getInstance() {
        // 第一次检查
        if (instance == null) {
            // 加锁保证线程安全
            synchronized (DoubleCheckSingleton.class) {
                // 再次检查防止同时进入synchronized
                if (instance == null) {
                    instance = new DoubleCheckSingleton();
                }
            }
        }
        return instance;
    }
}

使用 volatile 来修饰目的是为了使对于每个线程来说都是可见的。使用双重检查来避免重复的 new 操作。

静态内部类模式

package com.kikhot.design.singleton;


/**
 * 懒汉模式-静态内部类实现-线程安全
 * Singleton 类被装载了,instance 不一定被初始化
 * 因为 SingletonHandler 类没有被主动使用,只有通过显示调用 getInstance 方法时,才会显示装载 SingletonHolder 类
 */
public class InnerStaticSingleton {

    // 通过引入静态内部类 holder 来避免之前所介绍的,一加载就初始化的问题
    private static class SingletonHolder {
        private static final InnerStaticSingleton INSTANCE = new InnerStaticSingleton();
    }


    private InnerStaticSingleton() {
    }

    public static final InnerStaticSingleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

使用静态内部类方法来实现当没有使用 getInstance() 方法的时候,静态内部类没有被主动使用,所以 INSTANCE 并没有被实例化。当我们真正的调用 INSTANCE 的时候,才会进行加载,这样就保证了惰性的思想。

枚举模式

package com.kikhot.design.singleton;


/**
 * 饿汉模式-枚举实现-线程安全
 * 自动支持序列化机制,绝对防止多次实例化,避免多线程同步问题
 */
public enum EnumSingleton {

    INSTANCE;

    public void someMethod() {
        System.out.println("hello world");
    }
}

可以防止反射。

总结

在实际开发中,我们会比较青睐于使用第三种方式(饿汉模式)。虽然该方式会出现一些内存方面的性能问题,但是抛弃了锁的操作。枚举和双重判定的方式也是比较推荐的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值