【设计模式】单例模式

【概念】

单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例。

单例模式是设计模式中最简单的形式之一。这一模式的目的是使得类的一个对象成为系统中的唯一实例。要实现这一点,可以从客户端对其进行实例化开始。因此需要用一种只允许生成对象类的唯一实例的机制,“阻止”所有想要生成对象的访问。使用工厂方法来限制实例化过程。这个方法应该是静态方法(类方法),因为让类的实例去生成另一个唯一实例毫无意义。



【关键点】

显然单例模式的关键要素有三个:
1:是某个类只能有一个实例;
2:是它必须自行创建这个实例;
3:是它必须自行向整个系统提供这个实例。
从具体实现角度来说,就是以下三点:一是单例模式的类只提供私有的构造函数,二是类定义中含有一个该类的静态私有对象,三是该类提供了一个静态的公有的函数用于创建或获取它本身的静态私有对象。

【分类】

一、懒汉式单例

 像一个懒汉一样,什么时候用什么时候才创建对象。代码实现如下:

    //懒汉式单例类.在第一次调用的时候实例化自己   
    public class Singleton {  
        private Singleton() {}  
        private static Singleton single=null;  
        //静态工厂方法   
        public static Singleton getInstance() {  
             if (single == null) {    
                 single = new Singleton();  
             }    
            return single;  
        }  
    }  


但是以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个Singleton实例,要实现线程安全,有以下三种方式,都是对getInstance这个方法改造,保证了懒汉式单例的线程安全。

1、在getInstance方法上加同步

  1. public static synchronized Singleton getInstance() {  
  2.          if (single == null) {    
  3.              single = new Singleton();  
  4.          }    
  5.         return single;  
  6. }  

2、双重检查锁定

  1. public static Singleton getInstance() {  
  2.         if (singleton == null) {    
  3.             synchronized (Singleton.class) {    
  4.                if (singleton == null) {    
  5.                   singleton = new Singleton();   
  6.                }    
  7.             }    
  8.         }    
  9.         return singleton;   
  10.     }  

3、静态内部类

  1. public class Singleton {    
  2.     private static class LazyHolder {    
  3.        private static final Singleton INSTANCE = new Singleton();    
  4.     }    
  5.     private Singleton (){}    
  6.     public static final Singleton getInstance() {    
  7.        return LazyHolder.INSTANCE;    
  8.     }    
  9. }    
这种比上面1、2都好一些,既实现了线程安全,又避免了同步带来的性能影响。

二:饿汉式单例

    像个饿汉一样,类一加载就创建了对象,不管你以后是否用不用这个对象代码实现如下:

  1. public class Student {  
  2.     // 把构造方法私有,是为了不让外界随意创建对象  
  3.     private Student() {  
  4.     }  
  5.       
  6.     // 类本身创建一个唯一的对象。  
  7.     //加私有是为了不让外界通过类名直接访问s,进而修改s的值  
  8.     private static Student s = new Student();//饿汉式,类一加载就会创建这个唯一的对象  
  9.   
  10.     // 提供公共的方法让外界使用  
  11.     // 为了让外界能够直接通过类名访问该方法,需要对该方法加静态  
  12.     //因为静态方法只能访问静态成员,所以s也要用静态修饰  
  13.       
  14.     public static Student getStudent() {  
  15.         // 调用方法只能返回对象的引用  
  16.         return s;  
  17.     }  
  18. }
    这个时候,我们像如下调用时:
     public class SingleDemo{
              public static void main(String[ ] args){

        Student s1 = Student.getStudent();  
         Student s2 = Student.getStudent();  
        System.out.println(s1 == s2);
    }
    }


    打印出的返回值为true,也印证了单例模式作用下,类只能创建唯一的对象。


【饿汉式和懒汉式区别】

从名字上来说,饿汉和懒汉,

饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,

而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。

另外从以下两点再区分以下这两种方式:

1、线程安全:

饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,

懒汉式本身是非线程安全的,为了实现线程安全有几种写法,分别是上面的1、2、3,这三种实现在资源加载和性能方面有些区别。

2、资源加载和性能:

饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成,

而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了


  在懒汉式中,如果是在多线程环境下,有1号、2号、3号线程同时执行到了if (s == null)这一步,此时1号线程抢到了CPU的执行权,1号线程就通过了if(s==null)这一步,但是当1号执行s = new Student()之前,2号线程又抢到了CPU执行权,因为这时候1号还没有创建s指向的对象,所以2号线程也能通过if(s==null)的验证,来到s = new Student()这一步,从而就会创建两遍s指向的对象。

  那么线程安全问题如何解决呢?

  可以在可能出现问题的代码上加锁,锁上就行了,代码如下:

    <span style="font-size:14px;">public class Student {  
        private Student() {  
        }  
        private static Student s = null;  
        //在方法之上加锁  
        public synchronized static Student getStudent() {  
            if (s == null) {  
                s = new Student();  
            }  
            return s;  
        }  
    }  
    </span>  


【小结】

单例模式保证了一个类只能创建一个对象。

请使用手机"扫一扫"x
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值