单利设计模式提供了一种在多线程情况下保证实例唯一性的解决方案,以下简介7种单例模式
1,饿汉式
public final class Singleton {
//实例变量
private byte[] data=new byte[1024];
//定义实例对象的时候直接初始化
private static Singleton instance=new Singleton();
//利用构造函数不允许外部new
private Singleton(){
}
public static Singleton getInstance(){
return instance;
}
}
饿汉式的关键在于instacne作为类变量并且直接得到初始化,instance作为类变量在类初始化的过程中直接被创建实例对象,该方法能够百分之百的保证同步,在多线程情况下不可能被实例化两次,但是instance被classloader加载后可能很长一段时间内才被使用,也就是实例对象instance开辟的堆内存会驻留更久的时间。如果一个类中的成员属性较少,且占用内存资源不多,如果类成员属性都是比较重的资源,那么这种方式就有些欠妥。
2,懒汉式
public final class Singleton1 {
//实例变量
private byte[]data=new byte[1024];
//定义实例,但是不直接初始化
private static Singleton1 instance=null;
private Singleton1(){
}
public static Singleton1 getInstance(){
if(null==instance){
instance=new Singleton1();
}
return instance;
}
}
Singleton的类变量=null,因此当Singleton.class被初始化的时候instance并不会被实例化,在getInstance方法中会判断instance是否被实例化,但是将getInstance放在多线程的环境下进行分析,会导致instance被实例化不止一次,不能保证单例的唯一性。
3,懒汉式+同步方法
public final class Singleton1 { //实例变量 private byte[]data=new byte[1024]; //定义实例,但是不直接初始化 private static Singleton1 instance=null; private Singleton1(){ } public static synchronized Singleton1 getInstance(){ if(null==instance){ instance=new Singleton1(); } return instance; } }
采用懒汉式+同步方法的方式满足了懒加载又能够百分之百的保证instance实例的唯一性,但是synchronized关键字导致getInstance方法只能在同一时刻被一个线程访问,性能较低。
4,Double-Check
public final class Singleton2 {
//实例变量
private byte[]data=new byte[1024];
private static Singleton2 instance=null;
Connection conn;
Socket socket;
private Singleton2(){
//初始化conn
//初始化socket
}
public static Singleton2 getInstance(){
if(null==instance){
synchronized (Singleton2.class){
if(null==instance){
instance=new Singleton2();
}
}
}
return instance;
}
}
当连个线程发现null==instance成立时,只有一个线程有资格进入同步代码块,完成instance的初始化,随后线程发现null==instance不成立。以后对geiInstance的访问就不需要同步的保护了。
这种方法看起来满足了懒加载又提供了高效的数据同步策略,但是这种方式在多线程情况下会引起空指针异常,原因如下:在Singleton的构造函数中,需要分别实例化conn和socket两个资源,还有Singleton自身,根据JVM运行时的指令重排序和Happens-Before规则,这三者之间的实例化顺序并无前后关系的约束,如果instance最先实例化,为conn和socket未被实例化,则会抛空指针异常。
5,Volatile+Double-Check
使用Volatile可以防止重排序,代码如下:
public final class Singleton3 {
//实例变量
private byte[]data=new byte[1024];
private volatile static Singleton3 instance=null;
Connection conn;
Socket socket;
private Singleton3(){
//初始化conn
//初始化socket
}
public static Singleton3 getInstance(){
if(null==instance){
synchronized (Singleton3.class){
if(null==instance){
instance=new Singleton3();
}
}
}
return instance;
}
}
6,Holder方式
public final class Singleton4 { //实例变量 private byte[]data=new byte[1024]; private Singleton4(){ } //在静态内部类中持有Singleton4的实例,并且可被直接初始化 private static class Holder{ private static Singleton4 instance=new Singleton4(); } //调用getInstance方法,事实上是获得Holder的instance静态属性 public static Singleton4 getInstance(){ return Holder.instance; } }
在Singleton类中没有instance的静态成员,而是将其放到了静态内部类Holder中,因此在Singleton类的初始化过程中并不会创建Singleton的实例,Holder定义了静态变量,并且直接实例化,当Holder被主动引用后则会创建Singleton实例,同步方法既可以保证内存的可见性,顺序性和原子性,因此Holder是目前应用比较广泛的设计之一。
7,枚举方式
public class Singleton5 { //实例变量 private byte[] data = new byte[1024]; private Singleton5() { } //使用枚举充当hodler private enum EnumHolder { INSTANCE; private Singleton5 instance; EnumHolder() { this.instance = new Singleton5(); } private Singleton5 getInstance() { return instance; } } public static Singleton5 getInstance() { return EnumHolder.INSTANCE.getInstance(); } }
枚举方式是《Effective java》作者力推的方式,枚举类型不允许被继承,同样线程安全的且只能被实例化一次