设计模式之单例模式

定义:确保单例类只有一个实例,并且这个单例类提供一个函数接口让其他类获取到这个唯一的实例。其构造函数应该是private类型。
使用场景:如果某个类,创建时需要消耗很多资源,即new出这个类的代价很大;或者是这个类占用很多内存,如果创建太多这个类实例会导致内存占用太多。
最简单的单例模式:
public class Singleton{
    private static Singleton instance;
    //将默认的构造函数私有化,防止其他类手动new
    private Singleton(){};
    public static Singleton getInstance(){
        if(instance==null)
            instance=new Singleton();
         return instatnce;
    }
}
多线程环境下这代码明显不是线程安全的,存在隐患:某个线程拿到的 instance可能是 null,可能你会想,这有什么难得,直接在 getInstance()函数上加 sychronized关键字不就好了。可是你想过没有,每次调用 getInstance()时都要执行同步,这带来没必要的性能上的消耗。注意,在方法上加 sychronized关键字时,一个线程访问这个方法时,其他线程无法同时访问这个类其他 sychronized方法。
synchronized:
实际上,synchronized(this)以及非static的synchronized方法,只能防止多个线程同时执行同一个对象的同步代码段。
类方法中,synchronized锁住的是对象this,只有调用同一个对象的方法才需要获取锁。同时,同一个对象中所有加了synchronize的方法只能一次调用一个
static synchronized方法,static方法可以直接类名加方法名调用,方法中无法使用this,所以它锁的不是this,而是类的Class对象,所以,static synchronized方法也相当于全局锁,相当于锁住了代码段。
静态方法中,synchronized锁的是整个类对象,类似于(X.class),该类中所有加了synchronized的静态方法,一次只能调用一个
DCL双重检查锁定:
public class Singleton{
    private static Singleton instance;
    //将默认的构造函数私有化,防止其他类手动new
    private Singleton(){};
    public static Singleton getInstance(){
        if(instance==null){
            sychronized(Singleton.class){
                if(instance==null)
                    instance=new Singleton();
            }
        }
        return instatnce;
    }
}

为什么需要2次判断是否为空呢?第一次判断是为了避免不必要的同步,第二次判断是确保在此之前没有其他线程进入到sychronized块创建了新实例。这段代码看上去非常完美,但是,,,却有隐患!问题出现在哪呢?主要是在instance=new Singleton();这段代码上。这段代码会编译成多条指令,大致上做了3件事:

(1)给Singleton实例分配内存
(2)调用Singleton()构造函数,初始化成员字段
(3)将instance对象指向分配的内存(此时instance就不是null啦~)

上面的(2)和(3)的顺序无法得到保证的,也就是说,JVM可能先初始化实例字段再把instance指向具体的内存实例,也可能先把instance指向内存实例再对实例进行初始化成员字段。考虑这种情况:一开始,第一个线程执行instance=new Singleton();这句时,JVM先指向一个堆地址,而此时,又来了一个线程2,它发现instance不是null,就直接拿去用了,但是堆里面对单例对象的初始化并没有完成,最终出现错误~ 。

优点:资源利用率高,第一次执行getInstance时单例对象才会被实例化,效率高.


最终版:
public class Singleton{
    private volatile static Singleton instance;
    //将默认的构造函数私有化,防止其他类手动new
    private Singleton(){};
    public static Singleton getInstance(){
        if(instance==null){
            sychronized(Singleton.class){
                if(instance==null)
                    instance=new Singleton();
            }
        }
        return instatnce;
    }
}
volatile 关键字的作用是:线程每次使用到被 volatile 关键字修饰的变量时,都会去堆里拿最新的数据。换句话说,就是每次使用instance时,保证了instance是最新的。
public class Singleton {
	private Singleton (){
		
	}
	public static Singleton getInstance(){
		return SingletonHolder.SINSTANCE_SINGTON;
	}
	
	private static class SingletonHolder{
		private static final Singleton SINSTANCE_SINGTON=new Singleton();
	}
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值