美文网首页
设计模式---单例模式及其java实现

设计模式---单例模式及其java实现

作者: 何甜甜在吗 | 来源:发表于2017-12-17 14:32 被阅读0次

(一)什么是单例模式
确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实
一个简单的单例模式例子:Singleton1.java

public class Singleton1 {
    private static final Singleton1 singleton1 = new Singleton1(); 
    
    //访问权限为private,该方法不能被外面所访问到
    private Singleton1() {
    }
    
    public static Singleton1 getSingleton1 () {
        return singleton1;
    }
}

构造函数为private,则外界将不能通过new创建对象,只能通过调用getSingleton1()方法获得对象,并且这个对象是原来就创建好了的
这一种写法是单例模式中的饿汉式模式,在类加载的过程中就会对这个实例进行初始化,缺点就是不是懒加载的,不是在需要的时候才对它进行初始化
(二)单例模式的优点
1)只创建了一个实例,节省内存开销
2)减少了系统的性能开销
3)避免对资源的多重占用
4)在系统设置全局的访问点,优化和共享资源优化
(三)单例模式的缺点
1)不易于扩展,单例模式没有接口,不过接口单例模式好像是没什么意义的,单例模式的应用场景是生成对象啊,和接口扯不上边,而且接口是不能实例化的
2)对测试不利,因为不能使用mock的方式虚拟一个对象
(四)单例模式的应用场景
spring ioc使用单利模式创建bean,只是知道它是这么做的,因为没有研读spring源码,所以很多细节需要以后来补充
(五)并发条件下的单例模式
单例模式有很多种实现方式
上面只是其中一种,还有其他比如Singleton2.java

public class Singleton2 {
    private static Singleton2 singleton2 = null;
    
    private Singleton2() {}
    
    public static Singleton2 getSingleton2() {
        if (singleton2 == null) {
            singleton2 = new Singleton2();
        }
        
        return singleton2;
    }
}

Singleton1在并发的情况下是线程安全的,但Singleton2在并发情况下是线程不安全的,很有可能创建多个实例
修改方式:
1)在方法前面加上synchronized关键字,或者使用Lock来实现

 public static synchronized Singleton2 getSingleton2() {
        if (singleton2 == null) {
            singleton2 = new Singleton2();
        }

        return singleton2;
    }

2)同步代码块

 public static Singleton2 getSingleton2() {
        synchronized(singleton2) {
            if (singleton2 == null) {
                singleton2 = new Singleton2();
            }
        }
        return singleton2;
    }

目前比较熟悉的解决并发创建对象的方法,以后还要继续补充
上面两种虽然解决了线程安全,但并不高效
3)双重检查锁

public class Singleton3 {
    private static volatile Singleton3 singleton3 = null;

    private Singleton3() {}

    public static Singleton3 getSingleton2() {
        if (singleton3 == null) {
            synchronized (Singleton3.class) {
                if (singleton3 == null) {
                    singleton3 = new Singleton3();
                }
            }
        }

        return singleton3;
    }
}

为什么需要对singleton3添加volatile修饰符,因为singleton3 = new Singleton3();不是原子性的,分为三步
1.为singleton3分配内存
2.调用构造函数进行初始化
3.将singleton3对象指向分配的内存(执行完这步singleton3将不为null)
为了提高程序的运行效率,编译器会进行一个指令重排,线程1执行到= new Singleton3();线程2执行到if (singleton3 == null)假设进行了指令重排,2和3顺序相反,此时singleton3不为null,则线程2此时可能直接返回未正确进行初始化的singleton3对象。使用volatile可以禁止指令重排序
4)静态内部类

public class Singleton4 {
    private static class SingletonHolder {
        private static final Singleton4 singleton4 = new Singleton4();
    }
    
    private Singleton4() {}
    
    public static Singleton4 getSingleton4() {
        return SingletonHolder.singleton4;
    }
}

因为被static修饰,所以只会初始化一次,并且singleton4只会在访问该字段时初始化一次,所以它是懒汉式的
5)枚举方式

public enum Singleton5 {
    INSTANCE;

    public void hello() {
        System.out.println("hello, successful");
    }
}

public class Singleton5Test {
    public static void main(String[] args) {
        Singleton5 singleton5 = Singleton5.INSTANCE;
        singleton5.hello();
    }
}

代码相当优美
实现原理:枚举类的域(field)其实是相应的enum类型的一个实例对象
可以参考implementing-singleton-with-an-enum-in-java
(六)单例模式的扩展---多例模式
产生固定数量对象的模式。通过一个计数器实现

感觉自己的积累量还不够,很多东西都没办法想明白,比如这种设计模式的优缺点,为什么是这样的吗,如何解决,还有很长的路要走,厚积而薄发,切勿浮躁!!!

相关文章

网友评论

      本文标题:设计模式---单例模式及其java实现

      本文链接:https://www.haomeiwen.com/subject/ydbywxtx.html