单例设计模式:
单例模式是java中常见的一种设计模式,它确保一个类只有一个实例,也就是说采用单例模式的类(Singleton类)永远只会有一个实例化对象产生,而且该实例易于外界访问。这里主要介绍两种单例模式实现方式:懒汉式、饿汉式。
1) 懒汉式单例
线程安全,必须加锁,但效率低,第一次使用的时候实例化自己
/**
* 懒汉式单例类。 只在外部对象第一次请求实例的时候才会去创建
* 优点:第一次调用时才会初始化,避免内存浪费。
* 缺点:必须加锁synchronized 才能保证单例
*/
public class Singleton {
private static Singleton instance = null;
private Singleton() {
//Singleton通过将构造方法限定为private避免了类在外部被实例化
// 在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();//非原子操作
}
return instance;
}
}
此时没有考虑线程安全问题,所以通过对getInstance方法改造实现安全。
(1)在方法上加同步
public static synchronized Singleton getInstance() {//对获取实例的方法进行同步
if (instance == null) {//多线程中可能其他线程已经走到了后面,所以在这里先进行判空
instance = new Singleton();//非原子操作
}
return instance;
}
(2)双重检查锁定
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) { //判空后加锁
if (instance == null) {
instance = new Singleton();//非原子操作
}
}
}
return instance;
}
2)饿汉式单例
/**
* 饿汉式单例类。 它在类加载时就立即创建对象。
* 比较懒,在类加载时,不创建实例,因此类加载速度快,但运行时获取对象的速度慢
* 优点:在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全,
* 没有加锁,执行效率高。 用户体验上来说,比懒汉式要好。
* 缺点:类加载时就初始化,浪费内存
*/
public class Singleton {
private static Singleton instance = new Singleton();//直接初始化一个对象
private Singleton(){ //private类型的构造函数,保证其他类对象不能直接new一个该对象的实例
}
public static Singleton getInstance(){ //该类唯一的一个public方法
return instance;
}
}
3)比较这两种方法
- 懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例;饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了
- 懒汉式本身不是线程安全的,为了保证线程的安全性,可以利用加锁的方法,比如以上示例的两种;饿汉式天生线程安全,可以直接用于多线程而不会出现问题
- 懒汉式在第一次使用时才会实例化对象出来,第一次调用时初始化,所以性能上有些迟缓,效率会有点低;饿汉式创建类的同时就实例化一个静态对象,之后不管用不用都会占据内存,容易产生垃圾对象,耗费内存,但其调用时速度快,效率高