在面试的时候,少不了问单例模式,曾经有一次就被问到:你会几种单例模式的编写,当时只答出了普通的懒汉式和饿汉式,之后又问:多线程情况下,有考虑怎么写吗,回答说加上锁实现,却在写出来后被指出synchronized位置写错了,结果就这样黯然离开了,唉…所以写下这篇文章,引以为鉴!
单机下的单例
对于普通的单机环境下,单例模式的代码相信大家都知道,就不深入细讲,只把代码写出来,加深记忆:
// 懒汉式 即延迟加载
public class SingletonInstance {
//私有静态变量
private static SingletonInstance instance = null;
//私有构造
private SingletonInstance() {}
public static SingletonInstance getInstance() {
if (instance == null) {
return new SingletonInstance();
}
return instance;
}
}
// 饿汉式 即立即加载
public class SingletonInstance {
//私有静态变量
private static SingletonInstance instance = new SingletonInstance();
//私有构造
private SingletonInstance() {}
public static SingletonInstance getInstance() {
return instance;
}
}
单例模式,一定要注意:私有静态变量、私有构造方法、公共获取方法
集群下的单例
在集群环境下,如果还像上述方式写,就无法保证单例,因为多线程。在这种情况下,我们可以添加synchronized,以此来保证单例。
- 下面我先贴出曾经面试时错误的写法:
public class SingletonInstance {
//私有静态变量
private static SingletonInstance instance = null;
//私有构造
private SingletonInstance() {}
public static synchronized SingletonInstance getInstance() {
if (instance == null) { // 第一步
return new SingletonInstance(); //第二步
}
return instance; //第三步
}
}
上面这种写法虽然加了synchronized 关键字,但并没有真正起作用,比如:a线程走到第一步,b线程走到了第二步,即刚创建却但还没有返回,这时a线程就会继续走到第二步,这样就无法保证单例。
- 下面是正确的写法:
首先判断是否存在实例,如果不存在,就加锁创建实例,当a线程走到第一步,b线程走到第二步,a线程和b线程此时不冲突,都继续往下走,当b线程走到第三步时,就已经锁住对象,之后再判断是否已有线程走过第四步,即是否已经创建对象实例,如果没有,则走第四步,创建实例。
public class SingletonInstance {
//私有静态变量
private static SingletonInstance instance = null;
//私有构造
private SingletonInstance() {}
public static SingletonInstance getInstance() {
if (instance == null) { //第一步
synchronized (SingletonInstance.class) { //第二步
if (instance == null) { //第三步
return new SingletonInstance(); //第四步
}
}
}
return instance;
}
}