[Java] Java的单例和多例模式
单例模式和多例模式都是对象模式的一种。即对某些对象的全局实例化数量。
单例模式:一个类的对象在整个系统中只有一份实例,其他地方使用时需要通过类的静态get方法调用。例如后台中需要保存全局数据的某些类,亦或是controller这样负责处理相同事务的类。
多例模式:一个类的对象在整个系统中可以有多个实例,每个实例专门为一段时间的一段任务服务。
对于单例模式,通常是以private static的形式将类的实例化保存在类成员中,然后通过一些方式限制实例的获取。当然这个获取方法本身也是static的。
private static MyObject myObject;
public static MyObject getInstance() { ..., return myObject; }
单例模式有几种形式:
1)饿汉式
加载类代码时立即实例化其他类。
2)懒汉式
get获取实例时检查是否已经存在实例。若无则初始化,否则创建新的实例。
这也可以避免在启动时创建大量单例类的实例。
if (myObject != null) {
} else {
...,
myObject = new MyObject();
}
这样的缺点在于会造成线程不安全。为了避免线程竞争造成多次创建对象,可以给getInstance方法加sychronized锁。
给整个方法加上synchronized锁会导致性能严重下降,因此可以使用双重校验锁。
即只在创建类实例的时候对类类型加锁,这样之后的获取不会收到影响。
3)静态内置类实现单例模式
即在类内部使用一个静态类负责创建这个类的实例。看起来同样是饿汉模式。
private static class MyObjectHandler {private static MyObject myObject = new MyObject();}
public static MyObject getInstance() { return MyObjectHandler.myObject; }
这样做的原因是:
a. 加载类时,不会立即加载内部类。可以避免像饿汉模式那样有极高的启动时开销。
b. 仅当类的静态成员被调用,内部类才会被加载。这样可以确保线程安全(类加载一定是原子操作)
后续的改进还有,防止反射机制影响单例,以及防止序列化接口影响。
方法是使用一个静态的bool变量来表示类是否有实例被创建。
4)枚举类实现单例模式
Java中的枚举类类似于静态代码块,会在加载时自动调用构造方法。
一些常见的枚举数据类型(例如错误码等)常被枚举类型实现。
5)Spring中的单例和多例
spring中的bean默认为单例。且创建时为懒汉模式。
bean的作用域默认是"singleton", 即单例。
若为prototype,则总是会创建新的实例。
若为request,则bean的作用范围是request范围。
若为session,则bean的作用范围是session范围。
浙公网安备 33010602011771号