public class HungrySingleton {
int x = 1;
int j = 2;
private HungrySingleton instance = new HungrySingleton();
private HungrySingleton(){
}
public HungrySingleton getInstance() {
return instance;
}
}
饿汉式单例顾名思义,就是我很饿,我必须马上吃东西——主动使用HungrySingleton这个类的时候,instance就已经实例化,包括其中的实例变量也会初始化,性能比较高。
有一个缺点:无法实现延迟加载。
如果一个类中的成员变量比较少,占用内存比较少,就可以使用这种方法,但是如果一个类中的成员都是一些重量级的资源,而且加载后长时间都不会使用,那么这种方法就会有些不妥。
于是有了饿汉的对应:懒汉式:
public class LazySingleton {
int x = 1;
int j = 2;
private LazySingleton instance = null;
private LazySingleton(){
}
public LazySingleton getInstance() {
if(null==instance){
return new LazySingleton();
}
return instance;
}
}
懒汉式把实例化的操作放在了getInstance()里面,在类被初始化的时候并不会实例化instance,在getInstance()里面判断instance有没有被实例化,这样的操作看起来没什么问题,在单线程下的确做到了延迟加载和单例,但放在多线程的环境下就不一样了,如果两个线程在同一时间都通过了 null==instance的判断,那么就会实例两次。
于是有了懒汉式的增强版:懒汉式+同步方法
public class LazySyncSingleton {
private LazySyncSingleton instance= null;
private LazySyncSingleton(){
}
public synchronized LazySyncSingleton getInstance() {
if (null==instance){
return new LazySyncSingleton();
}
return instance;
}
}
在getInstance()方法上面加上同步约束,使得同一时刻只有一个线程能够访问这个方法,那么只要有一个线程完成实例化以后,后面的线程都不会再次实例化,解决了多线程的环境下百分之百只能够实例一次,但是由于synchronized关键字的排他性,导致多线程环境下性能低下。
双重检查单例模式:
public class DoubleCheckSingleton {
private Socket socket;
private volatile DoubleCheckSingleton instance = null;
private DoubleCheckSingleton() {
this.socket = new Socket();//实例化
}
public DoubleCheckSingleton getInstance() {
if (null == instance) {
synchronized (DoubleCheckSingleton.class) {
if (null == instance) {
return new DoubleCheckSingleton();
}
}
}
return instance;
}
}
这里使用了双重检查机制,当两个线程都通过了null == instance的判断,进入下一个判断时,只有一个线程能抢到锁进入同步块而完成实例化,完成实例化以后,其他的线程都不能通过第一次null == instance的判断,并不会像上一个模式一样,每次调用getInstance()方法时,都要进行同步,大大提高了多线程效率。
但这个方法还是有一个缺点,根据JVM运行时指令重排序和Happens-Before的规则,
instance和socket之间的实例化的顺序并没有什么关系约束,谁在前谁在后都有可能,如果instance最先实例化,那么socket就不会实例化,调用这个方法就会产生空指针异常。
解决这个方法很简单,加一个关键字 volatite
private volatile DoubleCheckSingleton instance = null;
这个关键字能够保证实例化的顺序的相对位置不变,instance实例化永远在socket之后。
public class HolderSingleton {
private HolderSingleton(){}
private static class Holder{
private static HolderSingleton instance = new HolderSingleton();
}
public static HolderSingleton getInstance() {
return Holder.instance;
}
}
这个方法并没有事先声明instance的静态成员,而是把它放到了静态内部类Holder中,Holder中定义了静态变量,并直接进行了实例化,当Holder被主动引用的时候就会创建实例。HolderSingleton在实例创建过程中被收集到<clinit>()方法中,这个方法是同步方法,保证了内存的可见性,JVM指令的顺序执行和原子性,该方法也是单例模式的最好设计之一。
package Singleton;
public class EnumSingleton {
private EnumSingleton() {
}
public enum Singleton {
INSTANCE;
private EnumSingleton instance;
private Singleton() {
this.instance = new EnumSingleton();
}
public EnumSingleton getInstance() {
return instance;
}
}
public EnumSingleton getInstance() {
return Singleton.INSTANCE.getInstance();
}
}
枚举类型不允许被继承,线程安全同时只能被实例化一次,是《Effective Java》作者力推的方式。
如果您发现该资源为电子书等存在侵权的资源或对该资源描述不正确等,可点击“私信”按钮向作者进行反馈;如作者无回复可进行平台仲裁,我们会在第一时间进行处理!
加入交流群
请使用微信扫一扫!