标签:
单例模式的写法
1. 懒汉模式(线程不安全)
懒汉模式:顾名思义就是需要使用的时候才实例化;
线程不安全:设想两个线程同时都运行到判断instance是否等于null的if语句,并且instance的确没有被创建,那么这两个线程都会创建一个实例(执行:instance = new Singleton1),此时就不满足单例模式的要求。
package Singleton; public class Singleton1 { private Singleton1(){}//private仅内部函数可以调用 private static Singleton1 instance = null; public static Singleton1 getInstance(){ if(instance == null){ instance = new Singleton1();//构造函数声明为private,仅内部函数可以调用,从而确保只创建一个实例 } return instance; }
}
2. 懒汉模式(线程安全)
线程安全:加上同步锁(synchronized)保证线程安全。此时若两个线程同时创建一个实例,由于在一个时刻只有一个线程能获得同步锁,当一个线程加上同步锁时,另一个线程只能等待。当第一个线程发现还没有创建实例时它创建一个实例。接着第一个线程释放同步锁,此时第二个线程可以加上同步锁,但发现此时已经创建过一个实例,就不会重复创建一个实例。从而保证单例模式。
package Singleton; public class Singleton2 { private Singleton2() {}// private仅内部函数可以调用 private static Singleton2 instance; public static synchronized Singleton2 getInstance() {//加同步锁,解决线程同步问题 if (instance == null) { instance = new Singleton2();// 构造函数声明为private,仅内部函数可以调用,从而确保只创建一个实例 } return instance; } }
3. 懒汉模式(双重校验锁)
考虑第2种方法由于加锁是一个耗时的操作,所以我们想办法减少加锁的次数。
本方法:如果实例已经被创建,直接返回即可;如果实例没有被创建,此时加锁保证线程安全再创建实例。
注意:关键字volatile
Volatile的作用: 强制每次都直接读内存,阻止重排序,确保 voltile 类型的值一旦被写入缓存必定会被立即更新到主存。
因为:在JAVA多线程环境下,每个Java线程除了共享的虚拟机栈外和Java堆之外,还存在一个独立私有的堆内存(默认情况下大小为512KB,在线程被创建时分配,可以通过-Xss选项调节其默认值大小)。每个线程独立运行,彼此之间都不可见,线程的私有堆内存中保留了一份主内存的拷贝,只有在特定需求的情况下才会与主存做交互(复制/刷新)。此时就会出现一种情况,虽然在某个线程私有的堆内存上已经创建出一个实例,由于线程还没有与主内存进行交互,导致主内存上还没有这个实例。此时用volatile就可以解决这个问题。
package Singleton; public class Singleton3 { private Singleton3() {}// private仅内部函数可以调用 private volatile static Singleton3 instance = null; public static synchronized Singleton3 getInstance() {//加同步锁,解决线程同步问题 if (instance == null) {//加锁耗时,减少加锁的次数 synchronized (Singleton3.class){ if (instance == null) { instance = new Singleton3();// 构造函数声明为private,仅内部函数可以调用,从而确保只创建一个实例 } } } return instance; } }
4. 饿汉模式
饿汉模式: 相对于懒汉模式,饿汉模式就是在类加载的时候就创建实例。
这种方式基于classloder机制避免了多线程的同步问题,instance在类装载时就实例化。下面就是饿汉模式的两种写法。
package Singleton; public class Singleton4 { private static Singleton4 instance = new Singleton4(); private Singleton4 (){} public static Singleton4 getInstance() { return instance; } }
package Singleton; public class Singleton5 { private static Singleton5 instance = null; static { instance = new Singleton5(); } private Singleton5 (){} public static Singleton5 getInstance() { return Singleton5.instance; } }
5. 静态内部类
这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,它跟饿汉模式不同的是:饿汉模式只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比饿汉模式就显得很合理。
package Singleton; public class Singleton6 { private static class SingletonHolder { private static final Singleton6 INSTANCE = new Singleton6(); } private Singleton6() {} public static final Singleton6 getInstance() { return SingletonHolder.INSTANCE;//单例模式推迟到SingletonHolder这个类加载 } }
6. 枚举
枚举:它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
package Singleton; public enum Singleton7 { INSTANCE; }
参考资料
标签:
原文地址:http://www.cnblogs.com/ChinaZT/p/4677302.html