一、饿汉式
/**
* 饿汉式单例
* 优点:类加载时就创建了变量对象,避免了线程同步问题;
* 缺点:如果一直没用到这个实例,那么造成内存浪费,没有实现懒加载。
*/
public class SingletonTest01 {
//单例模式第一步:私有化构造函数,让外部无法通过new来创建对象。
private SingletonTest01() {}
private final static SingletonTest01 mInstance = new SingletonTest01();
public static SingletonTest01 getInstance(){
return mInstance;
}
}
- 单例对象类第一步就是将构造方法私有化,防止外部通过new来创建对象;
- 优点:类加载时就创建了对象实例,避免了线程同步问题;
- 缺点:没有实现懒加载,类加载对象实例化之后如果一直没使用造成内存浪费。
二、懒汉式
1、线程不安全
public class SingletonTest02 {
private static SingletonTest02 mInstance;
//私有化构造器
private SingletonTest02(){
}
/**
* 实现了懒加载但线程不安全
* @return
*/
public static SingletonTest02 getInstance01(){
if (mInstance == null){
mInstance = new SingletonTest02();
}
return mInstance;
}
- 类加载的时候只是声明单例对象,在外部调用获取实例方法时再实例化,实例化对象之前先判空,为空再创建。第一步还是先将构造方法私有化;
- 优点:实现了懒加载,避免内存浪费;
- 缺点:线程不安全,当不同线程同时调用时,会出现创建出不同对象的情况,与单例模式相悖。
2、线程安全
public class SingletonTest02 {
private static SingletonTest02 mInstance;
//私有化构造器
private SingletonTest02(){
}
/**
* 添加synchronized关键字保证方法线程安全,但每一个调用的线程在上一个线程还未完结时都会被阻塞,当大量线程调用时耗时
* @return
*/
public static synchronized SingletonTest02 getInstance02(){
if (mInstance == null){
mInstance = new SingletonTest02();
}
return mInstance;
}
}
- 为解决第一种方法线程不安全的问题,在获取实例的方法加上synchronized关键字修饰,给方法加上同步锁;
- 优点:线程安全;
- 缺点:由于方法变成了同步,当多个线程同时调用时会出现阻塞,效率低。
三、双重检验锁
public class SingletonTest02 {
private static volatile SingletonTest02 mInstance;
//私有化构造器
private SingletonTest02(){
}
/**
* 线程不安全,一个线程进入到第一个if判断还未往下执行时,另一个线程也进入了if判断语句,此时两个线程同时到达synchronized,其中一个线程进去,另一个线程等待,
* 当先进去那个线程创建完成,走出方法后,在线程共享变量还未得到更新但另一个线程又进入了方法的时候,此时就会创建第二个对象,所以是非线程安全的。
* 要保证线程安全需在声明mInstance变量时加volatile关键字修饰,使得线程共享变量mInstance在线程读取时总会获得最新的状态,读总会发生在写之前。
*
* 双重检验锁创建单例对象满足了懒加载、线程安全、效率高等特点。
* @return
*/
public static SingletonTest02 getInstance03(){
if (mInstance == null){
synchronized (SingletonTest02.class){
if (mInstance == null){
mInstance = new SingletonTest02();
}
}
}
return mInstance;
}
}
- 为了解决懒汉式线程安全方法创建单例对象存在的效率低问题,这个方法将synchronized同步锁机制不加到方法上,而是在方法实现内部加上同步锁,在锁的内外做对象的双重判空,提升多线程调用的效率。需要注意的是,单例对象需要用volatile关键字修饰,使得线程共享对象在被读取时总是最新的状态,以此保证线程安全。
- 优点:保证了线程安全和效率。
四、静态内部类
/**
* 静态内部类创建单例对象
* SingletonTest03类在装载的时候,静态内部类SingletonHolder不会装载,只有在调用方法获取实例时才会去装载
* 静态内部类在装载时是线程安全的
*/
public class SingletonTest03 {
/**
* 私有化构造方法,防止外部通过new来创建对象
*/
private SingletonTest03(){
}
public static SingletonTest03 getInstance(){
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder{
private static final SingletonTest03 INSTANCE = new SingletonTest03();
}
}
- 静态内部类在外部类装载的时候不会装载,这样保证了懒加载。在调用时去装载内部类,同时去完成对象的创建,保证了线程安全;
- 优点:保证了线程安全和懒加载。
五、枚举
public enum SingletonTest04 {
INSTANCE;
public void hello(){
System.out.println("hello singleton enum");
}
}
- 优点:保证了线程安全,防止外部通过反射或者反序列化去创建对象。
总结
在开发中比较推荐使用饿汉式、双重校验锁、静态内部类、枚举去实现单一模式。