Java-设计模式:单例模式

Java单例模式详解
本文详细介绍了Java中单例模式的多种实现方式,包括饿汉式、懒汉式、静态内部类及单元素枚举等,并对每种方式进行了优缺点分析。

引言

程序运行中,往往会生成很多实例.当我们希望某个类只会存在一个实例的时候,单例模式便派上了用场.本篇将介绍在Java中,单例模式的几种实现方法并比较各自优缺点.

一丶 饿汉式写法

public class Singleton {
    /*饿汉式写法 直接在成员变量处实例化对象*/
    private static Singleton singleton = new Singleton();
    /*构造函数私有化 确保外界不能通过new关键字实例化对象*/
    private Singleton() {
    }
    /*暴露给外界的静态方法 用来获取Singleton类的实例*/
    public static Singleton getInstance(){
        return singleton;
    }
}

复制代码

可以看到饿汉式的写法简单粗暴,直接在静态成员变量处实例化对象,我们都知道,静态成员变量只会在类加载的时候被加载到静态域一次,因而能保证单例,并且是线程安全的。但是这种写法也有一个问题就是无论你是否使用到实例,实例总会被加载,白白浪费了资源,因此也有下面的这种写法.

二丶懒汉式写法

  • 常规写法,非线程安全

    public class Singleton {
        /*懒汉式 暂时不实例化*/
        private static Singleton singleton = null;
        /*构造函数私有化 确保外界不能通过new关键字实例化对象*/
        private Singleton() {
        }
        /*暴露给外界的静态方法 用来获取Singleton类的实例*/
        public static Singleton getInstance(){
            /*先进行判空操作 如果未空,进行实例*/
            if (singleton==null)
                singleton = new Singleton();
            return singleton;
        }
    }
    
    复制代码

    可以看到,懒汉式的写法是在第一次调用getInstance()方法时才实例化对象,一定程度上节省了资源.但是这种写法是线程不安全的。考虑这种情况:A和B两个线程同时进行到判空语句,此时对象还未被实例化,所以均返回true,然后A和B两个线程均会进行实例化操作,造成不可预知的错误.因此,该方法线程不安全.而下面的写法就是针对于这种写法进行的改进。

  • getInstance()方法加锁的写法,解决线程不安全的问题.

    public class Singleton {
        /*懒汉式 暂时不实例化*/
        private static Singleton singleton = null;
    
        /*构造函数私有化 确保外界不能通过new关键字实例化对象*/
        private Singleton() {
        }
    
        /*暴露给外界的静态方法 用来获取Singleton类的实例 对该方法加锁,解决同步问题*/
        public synchronized static Singleton getInstance() {
            /*先进行判空操作 如果未空,进行实例*/
            if (singleton == null)
                singleton = new Singleton();
            return singleton;
        }
    }
    
    复制代码

    这种方法之比于普通写法,对方法加锁,固然是解决了线程安全问题,但是每次调用getInstance()方法都要进行加锁和释放锁操作,也显得有点浪费资源,于是又诞生出了下面的写法.

  • 双重检查锁写法,解决线程不安全浪费资源的问题

    public class Singleton {
        /*懒汉式 暂时不实例化*/
        private static Singleton singleton = null;
    
        /*构造函数私有化 确保外界不能通过new关键字实例化对象*/
        private Singleton() {
        }
    
        /*暴露给外界的静态方法 用来获取Singleton类的实例*/
        public static Singleton getInstance() {
            /*先进行判空操作 如果未空,进行实例*/
            if (singleton == null) { 
                synchronized (Singleton.class) { 
                    if (singleton == null)
                        singleton = new Singleton();
                }
            }
            return singleton;
        }
    }
    
    复制代码

    这种写法被称为双重检查锁写法,双重检查是值对null进行两次检查,为什么要这样写呢?假如我们只检查一次会怎么样?也就是代码长这样:

    public class Singleton {
    /*懒汉式 暂时不实例化*/
    private static Singleton singleton = null;
    
    /*构造函数私有化 确保外界不能通过new关键字实例化对象*/
    private Singleton() {
    }
    
    /*暴露给外界的静态方法 用来获取Singleton类的实例*/
    public static Singleton getInstance() {
        /*先进行判空操作 如果未空,进行实例*/
        if (singleton == null) {
            synchronized (Singleton.class) {
                singleton = new Singleton();
            }
        }
        return singleton;
    }
    }
    复制代码

    对于这种写法,考虑这样一种情形:A和B线程同时执行到非空判断for循环内部,此时A拿到锁,先进行实例化操作,完成后,A释放,B又进行实例化操作,可以发现,如果不再次进行null判断,这种写法简直比普通写法还要糟糕,而如果多进行一次null判断,就比较完美了.

三丶静态内部类写法

public class Singleton {

    /*构造函数私有化 确保外界不能通过new关键字实例化对象*/
    private Singleton() {
    }

    /*暴露给外界的静态方法 用来获取Singleton类的实例*/
    public  static Singleton getInstance() {
    //静态内部类的特性,第一次调用该类才开始加载,达到了延迟加载的特性,同时线程安全
       return SingletonHelper.singleton;
    }

    private static class SingletonHelper{
    //静态成员变量 singleton的实例
        private static Singleton singleton = new Singleton();
    }
}

复制代码

关于这种写法有人会觉得和饿汉式的写法差不多,但这种写法由于静态内部类的加载机制,达到了延迟加载的效果,是优于饿汉式的,并且没有同步代码的开销,是比较推荐的写法.

四丶单元素枚举写法

public enum  Singleton {
    INSTANCE; //单元素
    public void fun1(){
    }
}

复制代码

写法也很简单,保证枚举中只有单个元素,然后这样调用:

                   Singleton.INSTANCE.fun1();
复制代码

Effective Java一书中对该方法的评价是:这种写法十分简洁,而且相比于其他写法,绝对的防止了多次实例化,即使是在面对复杂的序列化或者反射攻击的时候.总之,单元素的枚举类型已经成为实现Singleton的最佳方法.

总结

以上就是单例模式在Java中常见的几种写法啦,各种方法的优缺点相信大家也能有所了解,实际使用中,使用哪种方法还是看大家的个人习惯.在Kotlin中,一个Object关键字就能实现单例,也是十分的方便,大家有空可以了解了解.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值