单例模式:
定义:
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例
简单来说就是这个类只能有一个实例,必须自己创建自己的唯一实例,必须给其它所有对象提供这一实例
其实,所谓单例,指的就是单实例,有且仅有一个类实例,这个单例不应该由人来控制,而应该由代码来限制,强制单例。
1、常见的单例模式有两种创建方式:所谓饿懒汉式与饿汉式
(1)懒汉式
何为懒?顾名思义,就是不做事,这里也是同义,懒汉式就是不在系统加载时就创建类的单例,而是在第一次使用实例的时候再创建。
/**
* 懒汉式 - 单例设计模式
*/
public class LHSingleton {
private static LHSingletoninstance =null;
private LHSingleton () {}
//使用同步锁 synchronized 关键字,确保,在多线程情况下,不会产生多个类对象;解决了多线程环境下多个实例对象的问题
//缺点:每次执行getInstance方法时,都要同步判断,运行效率低,不可取
public static synchronized LHSingletongetInstance() {
if (instance ==null) {
return new LHSingleton();
}
return instance;
}
}
(2)饿汉式
在加载类的时候就会创建类的单例,并保存在类中。
/**
* 饿汉式 - 单例模式
*/
public class EHSingleton {
private static EHSingletoninstance =new EHSingleton();
private EHSingleton () {}
// 在类加载时,就实例化了单例对象,借用JVM来实现线程安全(不会创建多个单例对象)
// 缺点:占用较多的内存空间,不建议
public static EHSingletongetInstance() {
return instance;
}
}
2、双重加锁机制
何为双重加锁机制?
在懒汉式实现单例模式的代码中,有使用synchronized关键字来同步获取实例,保证单例的唯一性,但是上面的代码在每一次执行时都要进行同步和判断,无疑会拖慢速度,使用双重加锁机制正好可以解决这个问题:
/**
* 双重锁-懒加载(懒汉)- 单例模式
*/
public class DoubleSynchSingleton {
private static DoubleSynchSingletoninstance =null;
private DoubleSynchSingleton () {}
//双重加锁机制:不是加两个锁,是双重判断;
//优点:解决多线程环境下创建多个单例对象的情况
// 解决了懒汉模式加同步锁执行效率低的情况
//缺点:在Java指令中创建对象和赋值操作是分开进行的,即danli=new Danli()语句分两步执行,但JVM不能保证这两个操作的先后顺序,可能JVM会为新的Danli实例分配空间,然后直接赋值给Instance成员,然后再去初始化这个Danli实例,这样当其它线程访问到Danli实例时,因为没有初始化就会报错
// 虽然可以使用Volatile来禁止重排序,但是在JDK1.5之前Volatile不能解决重排序问题
// 所以建议,不可用
public static DoubleSynchSingletongetInstance() {
if (instance ==null) {
synchronized(DoubleSynchSingleton.class){
if (instance ==null) {
return instance;
}
}
}
return instance;
}
}
3、类级内部类方式
所谓类级内部类,就是静态内部类,这种内部类与其外部类之间并没有从属关系,加载外部类的时候,并不会同时加载其静态内部类,只有在发生调用的时候才会进行加载,加载的时候就会创建单例实例并返回,有效实现了懒加载(延迟加载),至于同步问题,我们采用和饿汉式同样的静态初始化器的方式,借助JVM来实现线程安全。
/**
* 静态内部类 - 单例模式
*/
public class StaticInnerClazzSingleton {
//JVM加载外部类时,不会同时加载静态内部类(即:外部类与其内部类并没有从属关系)
// 只有在发生调用的时候才会加载
private static class Inner {
private static StaticInnerClazzSingletoninstance =new StaticInnerClazzSingleton();
}
private StaticInnerClazzSingleton () {}
//使用 静态内部类方式创建单例,实现懒加载避免内存占用过多的问题;
//调用静态内部类,实例化单例对象,借助JVM避免了多线程环境下产生多实例的情况
// 目的:懒加载、线程安全
public static StaticInnerClazzSingletongetInstance() {
return Inner.instance;
}
}
总结:
一般采用饿汉式,若对资源十分在意可以采用静态内部类,不建议采用懒汉式及双重检测