Singleton 单例设计模式

本文详细介绍了Singleton单例设计模式的两种实现方式:饿汉式和懒汉式。重点阐述了它们的区别、特点及如何解决懒汉式在多线程环境下的问题。通过实例分析,展示了如何在单线程和多线程环境下正确使用单例模式,并讨论了同步机制的应用及其对程序效率的影响。
摘要由CSDN通过智能技术生成

---------------------- android培训java培训、期待与您交流! ----------------------

 

开发中,常用到Singleton 单例设计模式,他解决了一个类在内存中只能有一个对象,那么怎样保证对象的唯一性呢?

我们可以把这个类唯一的构造函数私有化,让他不能创建对象,也不能被继承,那怎样创建对象呢?只能在本类中先创建对象,

然后对外提供一个公共的获取实例的方法。

先看第一种写法,饿汉式的写法:

public class Singleton {
    private static Singleton instance = new Singleton();  
    //other fields…

    private Singleton() {
    }

    public static Singleton getInstance() {
        return instance;
    }
  
    //other methods…



1、  Singleton类的只有一个构造方法,它是被private修饰的,客户对象无法创建该类实例。 
2、 我们为此单例实现的全局访问点是public static Singleton getInstance()方法,注意,instance变量是私有的,外界无法访问的。 我们也可以定义instance变量是public的,这样把属性直接暴露给其他对象,就没必要实现public static Singleton getInstance()方法,但是可读性没有方法来的直接,而且把该实例变量的名字直接暴露给客户程序,增加了代码的耦合度,如果改变此变量名称,会引起客户类的改变。 如果该实例需要比较复杂的初始化过程时,最好把这个过程应该写在static{…}代码块中。 
3、  此实现是线程安全的,当多个线程同时去访问该类的getInstance()方法时,不会初始化多个不同的对象,这是因为,JVM(Java Virtual Machine)在加载此类时,对于static属性的初始化只能由一个线程执行且仅一次[1]
由于此单例提供了静态的公有方法,那么客户使用单例模式的代码也就非常简单了,如下所示:
Singleton singleton = Singleton.getInstance(); //这种写法比较简单常用,因为只有一条执行语句,不会产生多线程安全问题,所以也称作“饿汉式”

 

二、单例设计模式的另一种写法,懒汉式,从字面上理解,他比较懒,也就是这个类一初始化的时候并不会直接创建该类的实例,只有调用该类获取对象的方法是,才创建了该类对象,也称为对象的延迟加载,俗称“懒汉式”,那么我们可以这样写:

乍一看没问题,这个写法在单线程中确实没有问题,但是在多线程中,可能出现创建一个以上对象的情况:

class Single
{
 private static Single s = null;
 private Single(){}
 public static Single getInstance()
 {
       if(s==null)       // t1 t2两个线程
       {                       // Thread.sleep(10);假设 t1 cpu执行到这里, 这时t2线程进来了,也执行到这里挂了,t1醒了 new个对象,t2又醒了,他直接向下执行,也new 个对象
           s=new Single();  

        }

       return s;
 }
}

怎么解决呢?多线程同步函数可以解决,某一时刻,同步函数内只能有一个线程执行,必须这个线程执行完,另一个线程才能进来

class Single
{
 private static Single s = null;
 private Single(){}
 public static synchronized  Single getInstance()
 {
       if(s==null)      

       {                      
           s=new Single();  

      }

     return s;

 }
}

这样写就安全了,但程序的效率也大大降低了,因为任何一个线程进来都要判断锁,怎样才能减少判断锁的次数呢?我们只要把多个线程要访问的共享数据加锁就行了,

没必要整个函数都加锁,这样写:

class Single
{
 private static Single s = null;
 private Single(){}
 public static Single getInstance()
 {
  if(s==null)  //一旦new 对象,这就不为null,就不再判断锁了,相比同步函数大大提高了效率
  {
   synchronized(Single.class)   //静态的同步方法,使用的锁是该方法所在类的字节码文件对象。即类名.class
   {    
    if(s==null)
     s = new Single();
   }
  }
  return s;
 }
}

最后对单例设计模式的总结:

单例模式有两种:一种是饿汉式,另一种是懒汉式。
有什么区别(面试):懒汉式的特点在于实例的延迟加载。
懒汉式延迟加载有什么问题:有,如果多线程访问时会出现安全问题,怎么解决,可以加同步来解决;可以加同步来解决,用同步代码块和同步函数都行,但稍微有些低效,用双重判断能解决效率问题。
那么加同步的时候使用的锁是哪一个呢?该类所属的字节码文件对象。

注意:这里是在同一个JVM中,才能保证一个类只有一个单例,如果在分布式环境中,整个应用(可能分布在不同JVM上),这样写是不OK的。

 

---------------------- android培训java培训、期待与您交流! ----------------------详细请查看: http://edu.csdn.net/heima
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值