Java单例模式介绍
在java中,单例模式是最简单、最基础也是最常见的一种设计模式之一。
在运行期间,保证一个类只创造一个实例,并为它提供一个全局的访问点。
实现「单例模式」有以下几种常见方式
- 饿汉式
- 懒汉式
- 静态内部类
- 枚举类(推荐)
"Talk is cheap,show me the code."
饿汉式
public class Singleton {
private final static Singleton1 INSTANCE = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return INSTANCE;
}
}
饿汉式,顾名思义,就是肚子很饿,需要马上吃食物。这种写法,在类加载的时候就把实例给创建出来了,无需等待。
优点:实现简单、安全可靠
缺点:没有起到lazy load的效果
懒汉式
采用double check方式创建,保证线程安全「推荐」
public class Singleton {
private volatile static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
所谓懒汉式,就是懒加载。只有在需要调用它的时候才会创建实例
valatile作用:禁止重排序。
类在实例化的时候,可以分为三步骤
- 1.分配对象内存空间
- 2.初始化对象instance
- 3.设置instance指向刚刚分配的内存空间
在代码2和代码3中,这两个过程有可能会被jvm重新排序
这样在单线程环境是没有问题的,但在多线程环境下会出现以下问题:
线程B会看到一个未初始化的实例
所以使用valatile修饰instance变量,可以保证线程安全的初始化实例
静态内部类
public class Singleton {
private Singleton() {
}
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
使用内部类的好处:
静态内部类在不会在加载Singleton类的时候就加载,而是在调用getInstance()方法的时候才会加载,从而实现lazy load的目的
看似内部类创建单例的方式非常完美,但还是会存在反射攻击的问题,不够安全
枚举类
public enum Singleton8 {
INSTANCE;
public void whatever() {
System.out.println("I am Singleton");
}
}
利用枚举的特性,让jvm帮我们实现线程安全和单一实例的问题。写法还十分简单,强烈推荐
调用方法:
public class TestEnum {
public static void main(String[] args) {
Singleton8.INSTANCE.whatever();
}
}
总结
以上列举了创建单例模式的四种方法,同时还介绍了一种特别简单的写法——枚举。相信在未来,通过枚举创建单例的写法也会越来越流行。