单例会把默认的构造方法设为Private,让类自己来创建实例。。
第一种写法,饿汉式:
public class HungurySingleton { //缺点,无法延时加载,没有使用就已经加载了
private static final HungurySingleton mInstance = new HungurySingleton();
private HungurySingleton(){
}
public static HungurySingleton getHunguryInstance(){
return mInstance;
}
}
很明显,饿汉式在没有调用时就已经提前加载到了内存中,效率不是很高,无法实现延迟加载。。
第二种写法,懒汉式,优化了恶汉式无法延迟加载的问题
public class LazySingleton {//缺点,多线程并发的时候会失效,getmInstance不同步,例子:一个线程在创建mInstance时,还未创建完成,另一个线程访问mInstance此时还是为空,又创建了一次
private static LazySingleton mInstance;
private LazySingleton(){
}
public static LazySingleton getmInstance(){
if(mInstance == null){
mInstance = new LazySingleton();
}
return mInstance;
}
}
懒汉式进行了延迟加载,在调用getmIntance方法时才会去判断mInstance是否为空,如果为空,再去进行实例的创建。。但是在多线程操作的时候,可能会创建多个mInstance实例,比如在以下情况下:一个线程在创建mInstance实例时,mInstance实例还未创建完成,但是另外一个线程会去访问mInstance发现此时mInstance还是为空,这个时候该线程又会去再次创建一个mInstance实例,最后会有多个mInstance实例被创建,导致资源的浪费。
第三种写法,懒汉式线程安全。。。
public class SafeLazySingleton { //性能有缺陷
private static SafeLazySingleton mInstance;
private SafeLazySingleton(){
}
public static synchronized SafeLazySingleton getmInstance() {//在方法中声明同步方法
if(mInstance == null){
mInstance = new SafeLazySingleton();
}
return mInstance;
}
}
对懒汉式的优化,主要是在线程安全方面,有两种方式实现,上面第一种是使用synchronized关键字修饰,使得同时只能有一个线程访问
第二种是通过同步代码块来实现的
public class SafeLazySingleton { //性能有缺陷
private static SafeLazySingleton mInstance;
private SafeLazySingleton(){
}
public static SafeLazySingleton getmInstance(){ //同步代码块
synchronized (SafeLazySingleton.class){
if(mInstance == null){
mInstance = new SafeLazySingleton();
}
return mInstance;
}
}
}
实际和第一种方式差不多,只是稍微有些不同通过同步代码块来控制多线程的访问和操作。
以上这两种方法都是存在性能缺陷的,因为使用了synchronized关键字。
第四种写法 DCL双重检查锁,是对第三种方法性能缺陷的优化
public class DLCSingleton {
private static volatile DLCSingleton mInstance =null; //volatile关键字是为了禁止编译器对 volatile关键字修饰的变量进行重排序,并保证volatile变量的读操作发生在写操作之后
private DLCSingleton(){
}
public static DLCSingleton getmInstance(){
if(mInstance == null){ //第一次检查
synchronized (DLCSingleton.class){ //同步代码块
if(mInstance == null){ //第二次检查
mInstance = new DLCSingleton();
}
}
}
return mInstance;
}
}
DCL双重检查锁仅在真正创建mInstance实例的时候加上了synchronized关键字。。。而且使用volatile关键字修饰,是为了禁止编译器对volatile变量重排序,并且保证volatile变量的读操作发生在写操作之后。。
第五种,静态内部类的实现方式。。
public class StaticInnerSingleton { //完成了懒汉式的延迟加载,同时static保证了线程安全。
private StaticInnerSingleton(){
}
public static StaticInnerSingleton getIntance(){
return SingletonHolder.mIntance;
}
private static class SingletonHolder{ //私有的,初始化的时候,没有调用getIntance方法则不会加载
private static final StaticInnerSingleton mIntance = new StaticInnerSingleton(); //static,final是jvm提供的同步机制,初始化后就无法修改了
}
}
这种写法是最为推崇的写法,利用static final关键字的同步机制,初始化后就无法修改保证了线程安全。,使用holder的方式保证了延迟加载,不适用不会被加载。。。
第六种 枚举。枚举应该是think in java里面提到的,实际我发现没人这么用。很简洁,就几行代码
public enum EnumSingleton { //1,写法简单,2,线程安全。枚举出现在java5以后
INTANCE;
}
写法就两行,线程安全,枚举出现在JAVA5以后