单例模式四种实现方式
这里的单例模式实现方法都是线程安全的
一般常见的都是两种单例单例实现方法。加了两种,列举如下
- 饿汉式
- 双重检查加锁饿汉式
- 内部类方式
- 枚举单例
下面给出JAVA代码实例
/** * 懒汉式 */ public class Singleton1 { private Singleton1(){} private static final Singleton1 Instance = new Singleton1(); public static Singleton1 getInstance(){ return Instance; } public static void main(String[] args) { for (int i = 0; i < 100; i++) { new Thread( ()->{ System.out.println(Singleton1.getInstance().hashCode()); },"A").start(); } } }
/** * 双重检查的饿汉式--线程安全 */ public class Singleton2 { private Singleton2(){} public volatile static Singleton2 Instance; public static Singleton2 getInstance(){ if(Instance==null){ synchronized (Singleton2.class) { try { Thread.currentThread().sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } if(Instance==null){ Instance=new Singleton2(); } } } return Instance; } public static void main(String[] args) { for (int i = 0; i < 100; i++) { new Thread( ()->{ System.out.println(Singleton2.getInstance().hashCode()); },"B").start(); } } }
关于DCL的单例模式实现,为什么Instance要加volatile呢?synchronized已经可以保证内存可见性了。
其实是为了防止指令重排序出现问题。Instance = new Singleton2()分为三步。1、堆中申请new Singleton2()的内存空间; 2、对象new Singleton2()进行初始化;3、Instance指向内存地址
如果出现了指令重排序,可以预料到一个比较严重的问题:在第三步先执行后,此时进来另一个线程,它看到Instance不为空之后,可能就直接返回Instance对象,用Instance对象去执行一些操作【注意:此时Instance对象还未完成初始化】,就会出现线程不安全的问题。
/** * 单例类的静态内部类的构造方法只会在调用他的时候触发 * 所以时线程安全的 */ public class Singleton3 { private Singleton3(){} private static class holder{ private final static Singleton3 Instance = new Singleton3(); } public static Singleton3 getInstance(){ return holder.Instance; } public static void main(String[] args) { for (int i = 0; i < 100; i++) { new Thread(()->{ System.out.println(Singleton3.getInstance().hashCode()); },"C").start(); } } }
/** * 推荐方法: * java中,枚举类在编译后,文件中看,就是一个抽象类。所以没有构造方法。所以不存在线程安全问题 * 目前最好的方法,可以避免反序列化 */ public enum Singleton4 { Instance; public void m(){ System.out.println("我是枚举单例"); } public static void main(String[] args) { for (int i = 0; i < 100; i++) { System.out.println(Singleton4.Instance.hashCode()); } } }