把稳:
1、单例类只能有一个实例。2、单例类必须自己创建自己的唯一实例。3、单例类必须给所有其他工具供应这一实例。意图:担保一个类仅有一个实例,并供应一个访问它的全局访问点。
紧张办理:一个全局利用的类频繁地创建与销毁。
何时利用:当您想掌握实例数目,节省系统资源的时候。
如何办理:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
关键代码:布局函数是私有的。
下面是单例模式实现的几种办法。
一:静态内部类实现单例模式
事理:通过一个静态内部类定义一个静态变量来持有当前类实例,在类加载时就创建好,在利用时获取。缺陷:无法做到延迟创建工具,在类加载时进行创建会导致初始化韶光变长。
public class Singleton {
private static class xx {
private static Singleton singleton = new Singleton();
}
private Singleton(){}
public static Singleton getInatance(){
return xx.singleton;
}
}
二:饿汉模式
事理:创建好一个静态变量,每次利用时直接返回。缺陷:无法做到延迟创建工具,在类加载时进行创建会导致初始化韶光变长。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
三:
事理:延迟创建,在第一次用时才创建,之后每次用到就返回创建好的。缺陷:由于synchronized的存在,多线程时效率很低。
public class Singleton {
private static volatile Singleton instance;
private Singleton() { }
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
四:双重校验锁
事理:在getSingleton()方法中,进行两次null检讨。这样可以极大提升并发度,进而提升性能。
public class Singleton {
private static volatile Singleton uniqueInstance = null;//利用valatile,使该变量能被所有线程可见
private Singleton(){}
public static Singleton getSingleton(){
if(uniqueInstance == null){
synchronized (Singleton.class){//初始化时,加锁创建
if(uniqueInstance == null){//为避免在加锁过程中被其他线程创建了,再作一次非空校验
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
五:上述4种办法实现单例模式的缺陷
1、反序列化工具时会毁坏单例,反序列化工具时不会调用getXX()方法,于是绕过了确保单例的逻辑,直接天生了一个新的工具,毁坏了单例。办理办法是:重写类的反序列化方法,在反序列化方法中返回单例而不是创建一个新的工具。
public class Singleton implements Serializable {
public static Singleton instance = new Singleton();
private Singleton() {
}
//反序列时直接返回当前Instance
// 如果该工具被序列化,可以担保工具在序列化前后保持同等。
private Object readResolve() {
return Instance;
}
}
2、在代码中通过反射机制,直接调用类的私有布局函数创建新的工具,会毁坏单例,办理办法是:掩护一个volatile的标志变量在第一次创建实例时置为false;重写布局函数,根据标志变量决定是否许可创建。
private static volatile boolean flag = true;
private Singleton(){
if(flag){
flag = false; //第一次创建时,改变标志
}else{
throw new RuntimeException("The instance already exists !
");
}
六:列举模式实现单例——将单例掩护在列举类中作为唯一的实例。
事理:定义列举类型,里面只掩护一个实例,以此担保单例。每次取用时都从列举中取,而不会取到其他实例。
public enum SingletonEnum {
INSTANCE;
private String name;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
优点:1)利用SingletonEnum.INSTANCE进行访问,无需再定义getInstance方法和调用该方法。2)JVM对列举实例的唯一性,避免了上面提到的反序列化和反射机制破环单例的情形涌现:每一个列举类型和定义的列举变量在JVM中都是唯一的。
缘故原由:列举类型在序列化时仅仅是将列举工具的name属性输出到结果中,反序列化的时候则是通过java.lang.Enum的valueOf方法来根据名字查找列举工具。同时,编译器禁止重写列举类型的writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法。