经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » 设计模式 » 查看文章
设计模式之单例模式
来源:cnblogs  作者:知了一笑  时间:2019/7/8 8:52:19  对本文有异议

一、单例模式

1、概念图解

单例设计模式定义:确保这个类只有一个实例,并且自动的实例化向系统提供这个对象。

2、样例代码

  1. package com.model.test;
  2. public class Singleton {
  3. // 使用静态变量记录唯一实例
  4. private static Singleton singleton = null;
  5. private Singleton (){}
  6. public static Singleton getInstance (){
  7. if (singleton == null){
  8. singleton = new Singleton() ;
  9. }
  10. return singleton ;
  11. }
  12. public static void main(String[] args) {
  13. Singleton singleton1 = Singleton.getInstance() ;
  14. Singleton singleton2 = Singleton.getInstance() ;
  15. /**
  16. * com.model.test.Singleton@15db9742
  17. * com.model.test.Singleton@15db9742
  18. */
  19. System.out.println(singleton1);
  20. System.out.println(singleton2);
  21. }
  22. }

Singleton称为单例类,构造函数使用private修饰,确保系统中只能产生一个实例,并且自动生成的。上面代码也就是所谓的懒汉式加载:只有到使用该对象的时候才来创建,意思饿了才来做饭吃。

二、线程安全问题

在上面的代码中存在一个很明显的线程安全问题,当有多条线程来请求对象实例的时候,因为对象的创建是需要时间的,假设A线程进来判断singleton == null,就会进入对象的创建过程,这时如果同时在过来几条线程,那么他们都会得到一个对象实例,这个就是所谓的线程安全问题。

1、同步控制方式

  1. package com.model.test;
  2. public class Singleton {
  3. // 使用静态变量记录唯一实例
  4. private static Singleton singleton = null;
  5. private Singleton (){}
  6. public static synchronized Singleton getInstance (){
  7. if (singleton == null){
  8. singleton = new Singleton() ;
  9. }
  10. return singleton ;
  11. }
  12. }

这样操作会影响系统性能

2、饿汉式加载

  1. public class Singleton {
  2. // 使用静态变量记录唯一实例
  3. private static Singleton singleton = new Singleton();
  4. private Singleton (){}
  5. public static Singleton getInstance (){
  6. return singleton ;
  7. }
  8. }

这里先把对象创建出来,有需要直接使用;

3、双重检查

  1. public class Singleton {
  2. // 使用静态变量记录唯一实例
  3. // volatile可以确保当singleton被初始化后,多线程才可以正确处理
  4. // 被volatile修饰的变量的值,将不会被本地线程缓存
  5. // 对该变量读写都是直接操作共享内存,确保多个线程能正确的处理该变量。
  6. private static volatile Singleton singleton = null ;
  7. private Singleton (){}
  8. public static Singleton getInstance (){
  9. // 如果实例不存在,则进入同步区
  10. if (singleton == null){
  11. // 只有第一次才会彻底执行这里面的代码
  12. synchronized (Singleton.class) {
  13. if (singleton == null){
  14. singleton = new Singleton() ;
  15. }
  16. }
  17. }
  18. return singleton ;
  19. }
  20. }

三、延迟类初始化

1、基础概念

1)、类级内部类
  简单点说,类级内部类指的是,有static修饰的成员式内部类。如果没有static修饰的成员式内部类被称为对象级内部类。
  类级内部类相当于其外部类的static成分,它的对象与外部类对象间不存在依赖关系,因此可直接创建。而对象级内部类的实例,是绑定在外部对象实例中的。
  类级内部类中,可以定义静态的方法。在静态方法中只能够引用外部类中的静态成员方法或者成员变量。
  类级内部类相当于其外部类的成员,只有在第一次被使用的时候才被会装载。

2)、多线程缺省同步锁
  在多线程开发中,为了解决并发问题,主要是通过使用synchronized来加互斥锁进行同步控制。但是在某些情况中,JVM已经隐含地执行了同步,这些情况下就不用自己再来进行同步控制了。这些情况包括:

  1.   1.由静态初始化器(在静态字段上或static{}块中的初始化器)初始化数据时
  2.   2.访问final字段时
  3.   3.在创建线程之前创建对象时
  4.   4.线程可以看见它将要处理的对象时

3、实现方式

要想很简单地实现线程安全,可以采用静态初始化器的方式,它可以由JVM来保证线程的安全性。比如前面的饿汉式实现方式,在类装载的时候就初始化对象,不管是否需要,存在一定的空间浪费。
一种可行的方式就是采用类级内部类,在这个类级内部类里面去创建对象实例。这样一来,只要不使用到这个类级内部类,那就不会创建对象实例,从而同时实现延迟加载和线程安全。

  1. public class LazySingleton {
  2. /**
  3. * 类级内部类
  4. */
  5. private static class SingletonHolder {
  6. private static LazySingleton lazySingleton = new LazySingleton() ;
  7. }
  8. public static LazySingleton getInstance (){
  9. return SingletonHolder.lazySingleton ;
  10. }
  11. public static void main(String[] args) {
  12. LazySingleton lazySingleton1 = LazySingleton.getInstance() ;
  13. LazySingleton lazySingleton2 = LazySingleton.getInstance() ;
  14. /**
  15. * com.model.test.LazySingleton@15db9742
  16. * com.model.test.LazySingleton@15db9742
  17. */
  18. System.out.println(lazySingleton1+";;"+lazySingleton2);
  19. }
  20. }

四、Spring框架的单例模式

1、创建测试类

  1. public class UserBean {
  2. }

2、在Spring配置文件中配置

  1. <!-- 单例Bean -->
  2. <bean id="user" class="com.model.design.spring.node01.singleton.UserBean" />

3、测试程度读取Bean对象

  1. package com.model.design.spring.node01.singleton;
  2. import org.junit.Test;
  3. import org.springframework.context.ApplicationContext;
  4. import org.springframework.context.support.ClassPathXmlApplicationContext;
  5. /**
  6. * Spring框架中单例模式
  7. */
  8. public class S01_Singleton {
  9. @Test
  10. public void test01 (){
  11. ApplicationContext context01 = new ClassPathXmlApplicationContext("/spring/spring-context.xml");
  12. ApplicationContext context02 = new ClassPathXmlApplicationContext("/spring/spring-context.xml");
  13. UserBean user01 = (UserBean)context01.getBean("user") ;
  14. UserBean user02 = (UserBean)context01.getBean("user") ;
  15. UserBean user03 = (UserBean)context02.getBean("user") ;
  16. // com.model.design.spring.node01.singleton.UserBean@364841
  17. System.out.println(user01);
  18. // com.model.design.spring.node01.singleton.UserBean@364841
  19. System.out.println(user02);
  20. // com.model.design.spring.node01.singleton.UserBean@c4711c
  21. System.out.println(user03);
  22. }
  23. }

结论
Spring单例模式与纯粹的单例设计模式的主要区别
尽管使用相同的类加载器来加载两个应用程序上下文,但是UserBean的实例是不一样的。也就是Spring框架中的单例对象是基于应用程序中。

五、单例模式总结

优点:
1、单例模式只会创建一个对象实例,减少内存消耗
2、设置全局访问点,优化共享资源的访问

缺点:
1、没有接口,很难扩展
2、不利于测试
3、与单一职责原则冲突

六、源代码地址

  1. GitHub地址:知了一笑
  2. https://github.com/cicadasmile/model-arithmetic-parent
  3. 码云地址:知了一笑
  4. https://gitee.com/cicadasmile/model-arithmetic-parent


原文链接:http://www.cnblogs.com/cicada-smile/p/11146181.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号