Java的单例single经常用到,但是使用枚举enum最佳。
懒汉式单例
package com.mwq.singleton;
public class Singleton1 {
private Singleton1() {
}
private static Singleton1 single = null;
public static Singleton1 getInstance() {
if (single == null) {
return new Singleton1();
}
return single;
}
public String sayHello() {
return "hello singleton1" + this;
}
}
这种方式是在第一次使用的时候进行一次单例的实例化,我以前也习惯用这种方式,却不曾想这是线程不安全的,后面会写实例证明。对于private static Singleton1 single = null;
为什么没有加final关键字,因为这种懒汉式写法与final无关!
饿汉式单例
package com.mwq.singleton;
public class Singleton2 {
private Singleton2() {
}
private static final Singleton2 single = new Singleton2();
public static Singleton2 getInstance() {
return single;
}
public String sayHello() {
return "hello singleton2" + this;
}
}
这种方式在类创建的同时就进行了实例化,私有构造器仅仅被调用一次,这就保证它是安全的。
枚举式单例
package com.mwq.singleton;
public enum Singleton3 {
INSTANCE;
public String sayHello() {
return "hello singleton3" + this;
}
}
写法更快捷,我之前也未曾知道。
无偿地提供了序列化机制,绝对防止多次实例化,即使在面对复杂的序列化或者反射攻击的时候,无疑是最佳的singleton实现方式。—–effective Java一书
对于前面两种是有可能被借助setAcceessible()
方法进行反射攻击的,但是我没有找到怎么避免?????
论证
package com.mwq.singleton;
public class Test extends Thread{
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(new Test()).start();
}
}
@Override
public void run() {
System.out.println(Singleton1.getInstance().sayHello());
System.out.println(Singleton2.getInstance().sayHello());
System.out.println(Singleton3.INSTANCE.sayHello());
System.out.println();
}
}
通过以上的方法进行测试,得出如下内容:
hello singleton1com.mwq.singleton.Singleton1@1b1aa65
hello singleton2com.mwq.singleton.Singleton2@1125127
hello singleton3INSTANCE
hello singleton1com.mwq.singleton.Singleton1@bb7465
hello singleton1com.mwq.singleton.Singleton1@2a5330
hello singleton2com.mwq.singleton.Singleton2@1125127
hello singleton3INSTANCE
hello singleton2com.mwq.singleton.Singleton2@1125127
hello singleton3INSTANCE
可以看出来懒汉式单例的确是不安全的,会创建多次实例,第二种和第三种是安全的。
总结:本文参考了JAVA设计模式之单例模式博客,以及《effective Java 中文版》一书。