为什么使用?
确保一个类只有一个对象
怎样创建?
饿汉式单例模式
1.私有构造方法
2.定义一个类变量使其指向一个类对象
3.定义一个公共的·类方法,返回类变量,使外界可以调用
public class A {
//2.定义一个类变量记住一个对象
private static A a=new A();
//1.私有构造器
private A(){
}
//3.对外提供一个公共类方法,使其获得一个对象
public static A getInstance(){
return a;
}
}
饿汉式单例模式是在类加载时就创建好类对象,不管使不使用都会创建。
懒汉式单例模式
特点:只有在使用时才会创建。
懒汉式单例模式(线程不安全)
public class B {
//2.定义一个类变量用来保存对象
private static B b;
//1.私有构造器
private B(){
}
//3.对外提供一个公共类方法,使其获得一个对象
public static B getInstance(){
if(b==null){
b=new B();
}
return b;
}
}
从上面代码我们可以看出该方式在成员位置声明B类型的静态变量,并没有进行对象的赋值操作,那么什么时候赋值的呢?当调用getInstance()方法获取B类的对象的时候才创建B类的对象,这样就实现了懒加载的效果。但是,如果是多线程环境,会出现线程安全问题。
懒汉式单例模式(线程安全)
public class C {
//2.定义一个类变量用来保存对象
private static C c;
//1.私有构造器
private C(){
}
//3.对外提供一个公共类方法,使其获得一个对象
public static C getInstance(){
synchronized (C.class){
if(c==null){
c=new C();
}
return c;
}
}
}
该方式也实现了懒加载效果,同时又解决了线程安全问题。但是在getInstance()方法里添加了synchronized用来上锁,导致该方法的执行效果特别低。从上面代码我们可以看出,其实就是在初始化instance的时候才会出现线程安全问题,一旦初始化完成就不存在了。
懒汉式单例模式(双重检查锁)
public class D {
//2.定义一个类变量用来保存对象
private static D d;
//1.私有构造器
private D(){
}
//3.对外提供一个公共类方法,使其获得一个对象
public static D getInstance(){
if(d==null){
synchronized (D.class){
if(d==null){
d=new D();
}
}
}
return d;
}
}
双重检查锁模式是一种非常好的单例实现模式,解决了单例、性能、线程安全问题,但是双重检验锁可能会造成空指针异常,所以使用volatile关键字
懒汉式单例模式(双重检查锁+volatile )
public class E {
//2.定义一个类变量用来保存对象
//volatile 关键字可以保证可见性和有序性。
private static volatile E e;
//1.私有构造器
private E(){
}
//3.对外提供一个公共类方法,使其获得一个对象
public static E getInstance(){
if(e==null){
synchronized (E.class){
if(e==null){
e=new E();
}
}
}
return e;
}
}
添加 volatile 关键字之后的双重检查锁模式是一种比较好的单例实现模式,能够保证在多线程的情况下线程安全也不会有性能问题。