设计模式之单例模式(Java)
单例模式在Java中是比较常见的一个设计模式,同时也是比较基础、容易理解的设计模式。同过单例模式可以保证系统中只用一个目标类对象,每次调用获取的对象都是同一个。

单例模式的典型实现
单例模式根据实例初始化时机的不同又分为两种实现:饿汉模式懒汉模式
先看代码示例:饿汉模式
/**
 * 单例模式——饿汉模式
 * 类加载时创建实例,线程安全
 */
public class Singleton {
    
    //静态量,类加载时创建
    private static final Singleton instance = new Singleton();
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        return instance;
    } 
}

懒汉模式:
/**
 * 单例模式——懒汉模式
 * 调用时才实例化对象,非线程安全
 */
public class Singleton1 {

    private static Singleton1 instance;
    
    private Singleton1() {}
    
    public static Singleton1 getInstance() {
        if(instance == null) {
                        //调用时才创建实例
            instance = new Singleton1();
        }
        return instance;
    } 
}

饿汉模式:类加载时就初始化实例对象。特点是类加载比较慢,并且不支持延迟加载,但是调用时没有额外开销,速度较快线程安全
懒汉模式:第一次调用时才创建实例对象,特点是类加载较快,并且支持延迟加载,但是调用较慢(有额外的开销,比如if判断等)传统实现非线程安全。


那么有没有既支持延迟加载又是线程安全的实现方式呢?肯定是有的。下面总结了几种优雅的实现方式:
(1)利用静态内部类实现单例模式
静态内部类只有在被调用的时候才会装载,所以可以实现延迟加载,并且加载时创建实例对象,保证对象只实例化一次,确保了确保线程安全。
下面给出示例代码:
/**
 * 静态内部类实现单例模式
 * @author 12593
 *
 */
public class Singleton3 {

    private Singleton3() {}
    
    /**
     * 静态内部类,只有被调用时才会被装载
     */
    private static class SingletonHolder {
        private static Singleton3 instance = new Singleton3();
    }

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

(2)双重检查加锁机制
将创建实例的代码放到同步块中,并且在声明对象时加上volatile关键字。在获取实例时检查对象是否为空,为空则进入同步块,进入之后再次判断是否为空,为空则创建对象。
此处volatile关键字是必须的,否则在多线程执行时可能获取到不完整对象。试想有线程A和B,线程A获取对象实例发现对象为空,于是进入同步模块创建对象实例(instance = new Singleton2();),看似简单的一个新建对象操作在jvm中并不是一步完成的,如果在创建的过程中线程B开始请求对象,此时instance == null就是不成立的,所以就直接返回instance,但是这个instance其实是不完整的。这样就有问题了。
示例代码:
/**
 * 可延迟加载。 volatile关键字 + synchronized同步锁 保证 线程安全
 * @author 12593
 *
 */
public class Singleton2 {
    
    //注意 volatile关键字是必须的,否则有可能获得一个尚未完全构造完成的对象
    private static volatile Singleton2 instance; 
    
    private Singleton2() {}
    
    public static Singleton2 getInstance() {
        if(instance == null) {  //实例为空则进入同步块
            synchronized(Singleton.class) {
                if(instance == null) {  //同步块中再次检查实例是否为空
                    instance = new Singleton2();
                }
            }
        }
        return instance;
    }
}

(3)利用枚举实现
利用枚举实现单例简洁、高效且安全,是广泛推荐的实现方式。
/**
 * 枚举方式实现
 * 可以实现延迟加载,并且是线程安全的实现方式
 */
public class EnumSingleton {
    
    private EnumSingleton() {}
    
    public static EnumSingleton getInstance() {
        return Singleton.INSTANCE.getInstance();
    }
    
    private enum Singleton{
        INSTANCE;
        
        private EnumSingleton enumSingleton;
        
        private Singleton() {
            enumSingleton = new EnumSingleton();
        }
        
        public EnumSingleton getInstance() {
            return enumSingleton;
        }
    }
}

若您在阅读的过程中发现错误或者您有不同见解,欢迎留言交流学习。
It's
欢迎访问本站,欢迎留言、分享、点赞。愿您阅读愉快!
*转载请注明出处,严禁非法转载。
https://www.devsong.org
QQ留言 邮箱留言
头像
引用:
取消回复
提交
涂鸦
涂鸦
热门