单例模式

来源:互联网 发布:店铺seo是什么意思 编辑:程序博客网 时间:2024/06/07 01:42

概念

某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。单例模式主要有恶汉模式和懒汉模式。

恶汉模式

恶汉模式主要是在加载类的时候,就会实例化对象,具体代码如下:

public class Person {    private String name;    private int age;    private static final Person mPerson = new Person();    private Person(){}    public static Person getInstance(){        return mPerson;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }}

恶汉模式由于在加载类的时候就已经对对象进行初始化,因此这种实现方式不存在多线程不安全的问题。

懒汉模式

懒汉模式是声明一个静态对象,是在用户第一次调用获取对象时进行初始化。具体代码如下:

public class Person {    private String name;    private int age;    private static Person mPerson = null;    private Person(){}    public static Person getInstance(){        if(mPerson == null){            synchronized (Person.class){                if(mPerson == null){                    mPerson = new Person();                }            }        }        return mPerson;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }}

该写法亮点主要在于getInstance中的两次判空以及synchronized的用法。第一次判空这个很好理解,当单例对象不为空直接返回;使用synchronized主要是为了保证初始化对象时同步。第二次判空处理是什么意思呢?这个理解有点难度,需要从汇编的角度去思考。mPerson = new Person() 对应的汇编指令其实有多条,大致做了三件事情。

(1)、给Person的实例分配内存;

(2)、调用构造函数,初始化成员变量;

(3)、将mPerson对象指向分配的内存空间,此时mPerson就不是null了。

由于Java编译器允许处理器乱序执行,上面的2、3是无法保证顺序的,如果A线程先执行了3,此时B线程开始执行了,发现mPerson不为空,直接取走,在使用成员变量就会出错,因此上面这种单例实现方式还是存在一定问题的,而且这种错误很难重现。

下面给出另外一种实现方案,防止上面问题产生:

public class Person {    private String name;    private int age;    private Person(){}    public static Person getInstance(){        return PersonHolder.mPerson;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    private static class PersonHolder{        private static final Person mPerson = new Person();    }}

当第一次加载Person类时,并不会初始化mPerson,只有当第一次调用getInstance方法时才会初始化mPerson,这种方式不仅可以保证线程安全,也能保证单例对象的唯一性,同时也延迟了单例的实例化,所以上面这种方式才是推荐的使用方式。

对比恶汉模式与懒汉模式

1、恶汉模式在加载类的时候就已经初始化了对象,在类加载时就完成了初始化,所以类加载较慢同时这带来了内存的提前消耗;

2、懒汉模式在第一次需要的时候才会申请内存,这导致第一次加载的时候稍有缓慢,同时也要注意多线程的安全性,但是可以减少内存空间的使用。