饿汉式(线程安全)
public class SingletonEH {
private static SingletonEH instance = new SingletonEH();
private SingletonEH() {
}
public static SingletonEH getInstance() {
System.out.println("instance:" + instance);
System.out.println("加载饿汉式");
return instance;
}
}
在饿汉式中,只要类加载完成,就把单例化初始化完成了。保证在getInstance的时候,单例已经是存在的。
懒汉式(非线程安全)
public class SingletonLH {
private static SingletonLH instance;
private SingletonLH() {
}
public static SingletonLH getInstance() {
if (instance == null) {
instance = new SingletonLH();
}
return instance;
}
}
懒汉式比较懒,只有当调用getInstance的时候,才会对单例进行初始化
懒汉式非线程安全的解决办法就是在getInstance方法上添加synchronized就可以了
public class SingletonLHSync {
private static SingletonLHSync instance;
private SingletonLHSync() {
}
public static synchronized SingletonLHSync getInstance() {
if (instance == null) {
instance = new SingletonLHSync();
}
return instance;
}
}
饿汉式和懒汉式的比较
双重校验锁
- 加锁的懒汉式单例模式虽然可以保证线程安全,又实现了延迟加载,但是会影响效率
- synchronized修饰的方法执行较慢,如果多次调用getInstance就会造成性能下降
public class Singleton {
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance() {
if (instance == null) { // Single Checked
synchronized (Singleton.class) {
if (instance == null) { // Double checked
instance = new Singleton();
}
}
}
return instance;
}
}
- 同步代码块外层多了一层if判断,由于单例对象只需要创建一次,所以以后如果调用getInstance,就直接返回单例对象
- 大部分情况下,调用getInstance方法,都不会运行到同步代码块,从而保证性能提高
- 但是由于Java的指令重排,所以还是会有隐患
解决方案:
使用volatile变量,禁止指令重排
public class Singleton {
private static volatile Singleton instance = null;
private Singleton(){}
public static Singleton getInstance() {
if (instance == null) { // Single Checked
synchronized (Singleton.class) {
if (instance == null) { // Double checked
instance = new Singleton();
}
}
}
return instance;
}
}
静态内部类
public class Singleton{
private static class SingletonHolder{
public static Singleton instance = new Singleton();
}
private Singleton(){}
public static Singleton newInstance(){
return SingletonHolder.instance;
}
}
- 单例对象是在静态内部类中创建
- 其与饿汉式一样,利用类加载机制,保证线程安全
- 如果不调用静态内部类,就不会初始化单例对象,实现类似懒汉式的延迟加载
参考 Links: