1、普通单例
多线程环境下还是会创建多个对象。
2、使用sycnhronized关键字实现单例
使用sycnhronzied修饰创建对象的方法。只有一个线程能进方法去创建对象保证单例,这种方法没有下面方法的效率高。
3、双重检测单例模式
优点:在多线程的情况下最高性能的使用了锁机制保证了线程安全
缺点:创建对象有三个步骤,A1、分配对象的内存空间,A2、初始化对象,A3、设置引用指向内存空间。
如果出现A2与A3之间的指令从排序,A3设置引用指向内存空间后在进行A2操作。在执行A2之前其他线程获取到的引用相当于空引用,此时对象还没有初始化。
下面是伪代码:
memory=allocate(); //A1:分配对象的内存空间
ctorlnstance(memory); //A2初始化对象
instance =memory; //A3:设置instance指向刚分配的内存地址
弥补:设置成员变量Singleton 使用volatile关键字修饰,方式第3步和第二部指令从排序。
public class Singleton{
public Singleton(){}//属性私有化,保证只能在其他地方不能创建对象
public static volatile Singleton singleton; //使用volatile修饰防止指令重排序
public static Singleton getSingleton(){
if(singleton==null){
synchronized(Singleton.class){
if(singleton==null){
singleton=new Singleton();
}
}
}
return singleton;
}
}
4、内部类单例
优点:使用java虚拟机类加载机制进行单例的创建对象,在类加载的时候进行初始化。
因为在多线程环境下,jvm对一个类的初始化会做限制,同一时间只会允许一个线程去初始化一个类。
public class Singleton{
private Singleton(){};//构造方法私有化,禁止在其他地方创建对象
static{
System.out.println("This'sstaticcodeblock!");
}
//静态内部类进行对象创建
private static class SingletonHandler{
private static final Singleton INSTANCE = new Singleton();
static{
System.out.println("This'sinnerClass'sstaticcodeblock");
}
}
public static Singleton getInstance(){
return SingletonHandler.INSTANCE;
}
}