单例模式

定义:确保一个类只有一个实例,并且自行实例化并像整个系统提供该实例

 

 

特点:

1.私有的构造方法

2. 指向自己唯一实例的静态引用引用

3. 自己创建自己的唯一实例,并向外部提供该唯一实例

 

单例模式 常用的有懒汉模式 ,饿汉模式,静态内部类,枚举  饿汉模式在类被加载的时候就被实例化,而懒汉模式 是在使用的时候才被实例化

饿汉模式:(牺牲空间换取时间)

 

public class SingelDemo {
    private static SingelDemo single = new SingelDemo();
    private SingelDemo(){};
    public static SingelDemo newInstance(){
        return single;
    }
}

饿汉模式,是一种形象的称谓,既然饿,那么就会迫不及待的创建对象,在类装在的时候就创建了对象。

 

 

懒汉模式-同步方法:(始终用不到的单例无需加载)

 

public class SingleDemo1 {
    private static SingleDemo1 singleDemo1;
    private SingleDemo1(){}
    public static synchronized SingleDemo1 newInstance(){
        if(singleDemo1 == null){
            singleDemo1 = new SingleDemo1();
        }
        return singleDemo1;
    }
}

 

添加synchronized方法之后会影响性能,因此引入

懒汉模式-双重检查机制(注意需要volatile修饰)

public class SingleDemo1 {
    private static volatile SingleDemo1 singleDemo1;
    private SingleDemo1(){}
    public static  SingleDemo1 newInstance(){
        if(singleDemo1 != null){
            return  singleDemo1;
        }
        synchronized (SingleDemo1.class){
            if (singleDemo1 == null){
                singleDemo1 = new SingleDemo1();
            }
        }
        return singleDemo1;
    }
}

 

new 实例背后的指令

这个被忽略的问题在于 singleDemo1 = new SingleDemo1();这行代码并不是一个原子指令。使用 javap -c指令,可以快速查看字节码。

从字节码可以看到创建一个对象实例,可以分为三步:

分配对象内存

调用构造器方法,执行初始化

将对象引用赋值给变量。

虚拟机实际运行时,以上指令可能发生重排序。以上代码 2,3 可能发生重排序,但是并不会重排序 1 的顺序。也就是说 1 这个指令都需要先执行,因为 2,3 指令需要依托 1 指令执行结果。

Java 语言规规定了线程执行程序时需要遵守 intra-thread semantics。**intra-thread semantics ** 保证重排序不会改变单线程内的程序执行结果。这个重排序在没有改变单线程程序的执行结果的前提下,可以提高程序的执行性能。

虽然重排序并不影响单线程内的执行结果,但是在多线程的环境就带来一些问题。

上面错误双重检查锁定的示例代码中,如果线程 1 获取到锁进入创建对象实例,这个时候发生了指令重排序。当线程1 执行到 t3 时刻,线程 2 刚好进入,由于此时对象已经不为 Null,所以线程 2 可以自由访问该对象。然后该对象还未初始化,所以线程 2 访问时将会发生异常。

 

volatile 作用

正确的双重检查锁定模式需要需要使用 volatile。volatile主要包含两个功能。

保证可见性。使用 volatile定义的变量,将会保证对所有线程的可见性。

禁止指令重排序优化。

由于 volatile禁止对象创建时指令之间重排序,所以其他线程不会访问到一个未初始化的对象,从而保证安全性。

注意,volatile禁止指令重排序在 JDK 5 之后才被修复

 

静态内部类:

public class SingleDemo1 {
 
    private SingleDemo1(){}
    
    private static class InnerClass{
        private static final SingleDemo1 singleDemo = new SingleDemo1();
    }
    
    public static SingleDemo1 getInstance(){
        return InnerClass.singleDemo;
    }
}

静态内部类:内部类InnerClass 在外部类SingleDemo1 被类加载器加载时候 没有被加载,当调用 getInstance时候使用到内部类

InnerClass 时候,由类加载器加载(由类加载器确保了单例)实现了单例

枚举:

public enum  SingleDemo1 {

    INSTANCE;
}

单例模式的优点:

1.只有一个实例节省内存空间

2.避免了频繁 创建和销毁对象

懒汉模式 和饿汉模式对比:

1,饿汉模式 在初始化的时候创建对象,饿汉模式在使用的时候创建对象

2. 饿汉模式在初始化创建对象,天生的是线程安全的,懒汉模式可以采取 同步方法块但是性能不好,因此引入了双重检查机制,提高性能

 

(线程安全问题可以参考:http://blog.csdn.net/jason0539/article/details/23297037/)

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值