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
可以看出来懒汉式单例的确是不安全的,会创建多次实例,第二种和第三种是安全的。