设计模式 Singleton 单例 懒汉,线程安全 JAVA

        首先来明确一个问题,那就是在某些情况下,有些对象,我们只需要一个就可以了,比如,一台计算机上可以连好几个打印机,但是这个计算机上的打印程序只能有一个,这里就可以通过单例模式来避免两个打印作业同时输出到打印机中,即在整个的打印过程中我只有一个打印程序的实例。

 一、基本定义                                            

        通过上面简单介绍,我们可以对单例模式有一个简单的认识。所谓单例模式就是确保某一个类只有一个实例,并

且提供一个全局访问点。

        从上面可以看出单例模式有如下几个特点:

           一、它只有一个实例。

           二、它必须要自行实例化。

           三、它必须自行想整个系统提供访问点。

简单说来,单例模式(也叫单件模式)的作用就是保证在整个应用程序的生命周期中,

任何一个时刻,单例类的实例都只存在一个(当然也可以不存在)。

  二、实现                                                                        

public class SingletonClass {

  private static SingletonClass instance = null;
    
  public static SingletonClass getInstance() {
    if(instance == null) {
      instance = new SingletonClass();
    }
    return instance;
  }
    
  private SingletonClass() {
    
  }
    
}


同步:

上面的代码很清楚,也很简单。然而就像那句名言:“80%的错误都是由20%代码优化引起的”。单线程下,这段代码没有什么问题,可是如果是多线程,麻烦就来了。我们来分析一下:

线程A希望使用SingletonClass,调用getInstance()方法。因为是第一次调用,A就发现instance是null的,于是它开始创建实例,就在这个时候,CPU发生时间片切换,线程B开始执行,它要使用SingletonClass,调用getInstance()方法,同样检测到instance是null——注意,这是在A检测完之后切换的,也就是说A并没有来得及创建对象——因此B开始创建。B创建完成后,切换到A继续执行,因为它已经检测完了,所以A不会再检测一遍,它会直接创建对象。这样,线程A和B各自拥有一个SingletonClass的对象——单例失败

public class SingletonClass {
    
  private static class SingletonClassInstance {
    private static final SingletonClass instance = new SingletonClass();
  }

  public static SingletonClass getInstance() {
    return SingletonClassInstance.instance;
  }

  private SingletonClass() {

  }
    
}

三、多线程问题解决方案

这里有三种解决方案:

第一、  使用synchronized来处理。也就是说将getInstance()方法变成同步方法即可。

public class Singleton {
	//利用静态变量来记录Singleton的唯一实例
	private static Singleton uniqueInstance;
	
	/*
	 * 构造器私有化,只有Singleton类内才可以调用构造器
	 */
	private Singleton(){
		
	}
	// 同步 getInstance 方法
	public static synchronized Singleton getInstance(){
		if(uniqueInstance == null){
			uniqueInstance = new Singleton();
		}
		
		return uniqueInstance;
	}
	
}

第二、  懒加载 直接初始化静态变量。这样就保证了线程安全

public class Singleton {
    /*
     * 利用静态变量来记录Singleton的唯一实例
     * 直接初始化静态变量,这样就可以确保线程安全了
     */
    private static Singleton uniqueInstance = new Singleton();
    
    /*
     * 构造器私有化,只有Singleton类内才可以调用构造器
     */
    private Singleton(){
        
    }
    
    public static Singleton getInstance(){
        return uniqueInstance;
    }
    
}

 第三、  用“双重检查加锁”,在getInstance()中减少使用同步

public class Singleton {
	/*
	 * 利用静态变量来记录Singleton的唯一实例
	 * volatile 关键字确保:当uniqueInstance变量被初始化成Singleton实例时,
	 * 多个线程正确地处理uniqueInstance变量
	 * 
	 */
	private volatile static Singleton uniqueInstance;
	
	/*
	 * 构造器私有化,只有Singleton类内才可以调用构造器
	 */
	private Singleton(){
		
	}
	
	/*
	 * 
	 * 检查实例,如果不存在,就进入同步区域
	 */
	public static Singleton getInstance(){
		if(uniqueInstance == null){
			synchronized(Singleton.class){    //进入同步区域
				if(uniqueInstance == null){     //在检查一次,如果为null,则创建
					uniqueInstance  = new Singleton();
				}
			}
		}
		
		return uniqueInstance;
	}
	
} 

很显然第三种是最好的,一,如果不使用不会占用内存,二 效率也比较高;

  四、模式优缺点                                                                                                           

           优点

              一、节约了系统资源。由于系统中只存在一个实例对象,对与一些需要频繁创建和销毁对象的系统而言,单

例模式无疑节约了系统资源和提高了系统的性能。

             二、因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它。

         缺点

             一、由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。

             二、单例类的职责过重,在一定程度上违背了“单一职责原则”。

           五、模式使用场景                                                                                                     

           下列几种情况可以使用单例模式。

              一、系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器,或者需要考虑资源消耗太大而只允许创建一个对象。

             二、客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。

           六、总结                                                                                                                    

           1. 单例模式中确保程序中一个类最多只有一个实例。

           2. 单例模式的构造器是私有了,而且它必须要提供实例的全局访问点。

           3. 单例模式可能会因为多线程的问题而带来安全隐患。

             一、由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。

             二、单例类的职责过重,在一定程度上违背了“单一职责原则”。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

恋恋西风

up up up

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值