单例模式浅谈

所谓单例模式,就是让一个类在该类的外面只有一个实例
我们知道我们一旦把一个类给声明好了,它有多少个实例,取决于我们new多少次
那什么情况下能让一个类在该类的外面只有一个实例呢?
看下面代码:

class Foo{
    private Foo(){}
}
public class Test{
    public static void main(String[] args){ 
    }
}

Ok,为了不让它在外面new,干脆把构造器设置为private,保证了不能new多个,但是一个没有也不合适!!
问题又出现了,这样做的结果是“没例模式”!!!
好吧,这样做虽然外面不能new多个,但是在Foo类里面是可以new的

class Foo{
    private Foo(){}
    public void f1(){
        Foo foo1 = new Foo();
        Foo foo2 = new Foo();
        Foo foo3 = new Foo();
    }
}
public class Test{
    public static void main(String[] args){
    }
}

不能这么去理解,我们所说的单例模式是:不要在Foo类里面new ,而是在Foo类外面只有一个实例!
那肿么办呢?
往下看:

class Foo{
    //饱汉式
    private static Foo instance = new Foo();
    private Foo(){}
    public static Foo getInstance(){
        return instance;
    }
}
public class Test{
    public static void main(String[] args){ 
        Foo f1 = Foo.getInstance();
        Foo f2 = Foo.getInstance();
        System.out.println(f1 == f2);
    }
}

解释一下吧:

private static Foo instance = new Foo();
//当一个类作为另一个类的属性时,叫做关联,而另一个类又是它自己时,就构成了自关联
//将这个属性设置为static ,静态属性隶属于静态块
//而静态块在整个程序运行期间,只会运行一次,那么什么时候运行呢?
//调用一个类的静态属性、静态方法时,或是在外面new的时候
//而现在在外面调用了Foo的静态方法getInstance()

现在在main方法里第一次调用了getInstance()方法,这个时候会去加载类
加载类的时候,先走静态块,也就是先执行上面的属性,此时new一个实例,再往下走,经过了构造器,构造器里什么也没执行
最后再执行对外暴露的一个公共方法,返回一个实例
当第二次调用getInstance()方法时,静态块不会再重新加载,保证了只new一次,这次的引用指向的仍是之前的对象
所以最后我们在比较f1 == f2两个的引用地址时,结果是true;
那么我们先把这种称为“饱汉式”,因为它一进来就new了
看下面的懒汉式:

class Foo{
    //懒汉式
    private static Foo instance = null;
    private Foo(){}
    public static Foo getInstance(){
        if(instance == null){
            instance = new Foo();
        }
        return instance;
    }
}
public class Test{
    public static void main(String[] args){ 
        Foo f1 = Foo.getInstance();
        Foo f2 = Foo.getInstance();
        System.out.println(f1 == f2);
    }
}

分析一下:
我们知道静态属性是隶属于整个类,而不是属于单独的某个实例
当在main方法里第一次调用getInstance()方法时,过程和上面一样,第一次是null,此时创建一个实例,当第二次调用getInstance()方法时,此时的instance已不是null,不会在进if(),保证了只new一次
由于这种方式,在用的时候,才去new,不用的时候,不new,就把它称为“懒汉式

现在这个程序有点小小的问题,假设这个方法被两个线程调用,当第一个线程顺利通过了if(),正准备new实例时,是有机率被第二个线程所打断的,第二个线程此时new了一个实例,而当此时第一个线程再次努力的抢占cpu成功,它会再从这里继续往下执行,也new一个实例,这个时候,问题就出现了,这就不叫单例了……………
OK,问题暴露出来了,肿么办呢?
现在只能加对象互斥锁了

public static Foo getInstance(){
        synchronized(Foo.class){
            if(instance == null){
                instance = new Foo();
            }
        }
        return instance;
}

当第一次调用getInstance()方法,第一个线程获得了锁,顺利通过if(),此时被第二个线程抢走了cpu,但是第二个线程得不到锁,因为锁在第一个线程手里,所以现在第二个线程只能放弃cpu,让第一个线程顺利往下执行,执行完后,释放cpu,如果另外的线程再拿到锁时,此时instance 已经不是null,就不会再去new了,保证了只new一次,单例模式就OK了

但是它还不够完美,因为每次一个线程过来时候,都要加锁,其实只要第一次加锁就够了,当instance != null,时就没有必要加锁了
所以看下面:

public static Foo getInstance(){
    if(instance == null){//保证只加一次锁
        synchronized(Foo.class){
            if(instance == null){//保证只new一次
                instance = new Foo();
            }
        }
    }
        return instance;
}

好了,这就是双重检测机制,现在看起来有点完美了
如有瑕疵,请多指教…………

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值