对一些重要资源的访问, 有时需要实例创建只有一份, 那么实例创建就需要用到单例模式 .
恶汉式:
在类初始化时候就加载了对象, 可以不用考虑多线程问题 .
缺点 : 如果对象创建比较耗资源, 提前加载会耗费性能 .
/**
* 恶汉式
* @author 张延
* */
public class SingleInstance {
// 类初始化就加载这个对象, JVM初始化是线程安全的
private static SingleInstance singleInstance = new SingleInstance();
// 私有化构造器
private SingleInstance(){};
// 提供一个公共的获取对象方法
public static SingleInstance getInstance() {
return singleInstance;
}
}
懒汉式:
- 不加锁
多线程情况会出现问题, 可能会导致两份实例创建
/**
* 懒汉式
* @author 张延
* */
public class SingleInstance {
private static SingleInstance singleInstance = null;
private SingleInstance(){};
public static SingleInstance getInstance() {
if(singleInstance == null) {
singleInstance = new SingleInstance();
}
return singleInstance;
}
}
- 加锁方式
针对上面不安全方式改进, 但是这种synchronized修饰方法会导致性能降低, 因为getInstance是一个频繁被使用方法,
/**
* 加锁的懒汉式
* @author 张延
* */
public class SingleInstance1 {
private static SingleInstance1 singleInstance = null;
private SingleInstance1() {};
// 在方法上加锁,限制多个线程同时进入,导致实例化不是一份
public synchronized static SingleInstance1 getInstance() {
if(singleInstance == null) {
singleInstance = new SingleInstance1();
}
return singleInstance;
}
}
- 双重锁检查
将锁的粒度降低, 多线程都可以进去判断”null == singleInstance” , 只有当没有实例创建才会进入同步快, 再次检查是否创建, 如果没有创建,才会创建这个实例 .
/**
* 双重锁检查的懒汉式
* @author 张延
*
*/
public class SingleInstance2 {
private volatile static SingleInstance2 singleInstance = null;
private SingleInstance2() {};
public static SingleInstance2 getInstance() {
// 多线程可以进来判断是否为null,只有当没有创建实例,才会进入同步代码快
if(singleInstance == null) {
// 将锁的粒度降低,
synchronized (SingleInstance2.class) {
if(singleInstance == null) {
singleInstance = new SingleInstance2();
}
}
}
return singleInstance;
}
}
静态内部类方式:
基本具备很多优点 , 高校调用 , 延时加载 , 线程安全
SingleInstance类被加载, 但是instance不会被初始化, 可以做到延时加载, 只有当主动获取实例变量, 才会显示加载类, 因为JVM是天然线程安全的, 不会加载两份, 所以实例化instance很耗资源, 应该让他延时加载 .
public class SingleInstance {
// 初始化SingleInstance的时候,内部类InnerClass不会被加载,只有在被主动使用才会加载
private static class InnerClass {
private static SingleInstance singleInstance = new SingleInstance();
}
private SingleInstance() {};
public static SingleInstance getInstance() {
// 当这里主动使用, 才会加载InnerClass,才会创建SingleInstance实例
return InnerClass.singleInstance;
}
}
总结:
饿汉式 : 线程安全 , 调用效率高 , 但是不能延时加载
懒汉式(加锁) : 线程安全 , 调用效率不高 , 但是可以延时加载
静态内部类 : 线程安全 , 调用效率高 , 并且还可以延时加载