Java设计模式——单例模式的实现
对于单例模式的实现,在Java面试中也是会经常问到,单例模式有好多种实现方法,让我在下面给大家展开讲解
1.饿汉模式
class Singleton1 { // 饿汉模式
private static Singleton1 singleton1 = new Singleton1();
private Singleton1(){}
public static Singleton1 getInstance(){
return singleton1;
}
}
在这种模式下,先定义一个static修饰(用static修饰就可以保证唯一性,保证所有类对象使用的都是这一个singleton)的singleton对象并实例化,甭管用不用,先实例化,这就是饿汉模式,
缺点就是浪费内存。但是优点是这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。
2.懒汉模式
class Singleton2 { // 懒汉模式
private static Singleton2 singleton2;
private Singleton2(){
if (singleton2 == null)
singleton2 = new Singleton2();
}
public static Singleton2 getInstance(){
return singleton2;
}
}
懒汉模式的实现是有一个私有的构造方法,当你尝试去实例化这个类的时候,他会自动new一个singleton出来,用的时候再去实例化,这就是懒汉模式
当然这种模式有什么缺点呢?那就是线程不安全,当你对面试官回答这两种实现方法的时候,面试官肯定会继续问你,那如何保证单例模式线程安全呢?
让我们往下看
3.双重检查锁
class Singleton3 { // 双重检查锁
private static Singleton3 singleton3;
private Singleton3(){}
public static Singleton3 getInstance(){
if (singleton3== null){
synchronized (Singleton3.class){
if (singleton3== null)
singleton3 = new Singleton3();
}
}
return singleton3;
}
}
首先判断singleton是否被实例化,如果没有的话,使用synchronized获取类锁,保证没有别的线程可以操作这个类,然后再去判断锁这个类的时候singleton是否被实例化,如果没有,那就new一个
但是这又会有一个什么问题呢?
假设线程A执行到了第1个if,它判断对象为空,于是线程A继续执行去初始化这个对象,但初始化是需要耗费时间的,但是这个对象的地址其实已经存在了。此时线程B也执行到了第1个if,它判断不为空,于是直接跳到后面得到了这个对象。但是,这个对象还没有被完整的初始化!得到一个没有初始化完全的对象有什么用!!
所以这时候还有一种解决办法
4.静态初始化块
class Singleton4{
private static class SingletonHolder
{
public final static Singleton4 instance = new Singleton4();
}
public static Singleton4 getInstance()
{
return SingletonHolder.instance;
}
}
这种方法使用内部类来做到延迟加载对象,在初始化这个内部类的时候,JLS(Java Language Sepcification)会保证这个类的线程安全。这种写法最大的美在于,完全使用了Java虚拟机的机制进行同步保证,没有一个同步的关键字。