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

(一)什么是单例模式
确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实
一个简单的单例模式例子: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
(六)单例模式的扩展---多例模式
产生固定数量对象的模式。通过一个计数器实现

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,859评论 18 139
  • 单例模式(Singleton):在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有...
    _SHYII阅读 832评论 0 2
  • 单例模式 介绍 为了节约系统资源,有时需要确保系统中某个类只有唯一一个实例,当这个唯一实例创建成功之后,我们无法再...
    666真666阅读 359评论 0 6
  • 目录 本文的结构如下: 什么是单例模式 为什么要用该模式 模式的结构 代码示例 优点和缺点 适用环境 模式应用 总...
    w1992wishes阅读 402评论 1 2
  • 木坑以竹海闻名,地处桃源山村东大门,为黄山入黟第一寨,也是电影《卧虎藏龙》竹林打斗场景的拍摄地。木坑竹海地处宏村东...
    高鑫丽阅读 923评论 0 0