保证系统内存只有一个对象,节省系统资源,对于需要频繁创建销毁的对象,使用单例模式可以提高性能
提供一个获取对象的方法,而不是直接new
单例模式主要分为两大类
- 饿汉模式:类装载的时候就创建对象
- 懒汉模式:需要用到对象的时候创建
使用单例模式,首先是要私有化构造器
单例模式各种线程安全和不安全的写法加起来一共有八种写法
防止反射调用
可在私有化的无参构造方法里加一段,调用无参构造直接抛异常
if(instance != null){
throw new RuntimeException("单例模式禁止反射调用");
}
饿汉模式写法
通常使用静态static关键字
public class StaticSingletonTest {
private final static StaticSingletonTest instance = new StaticSingletonTest();
private StaticSingletonTest() {
if(instance != null){
throw new RuntimeException("单例模式禁止反射调用");
}
}
public static StaticSingletonTest getInstance() {
return instance;
}
}
也可以使用静态代码块来写
注意:静态代码块是按照顺序执行的!!!
private final static StaticSingletonTest instance;
static {
instance = new StaticSingletonTest();
}
public static StaticSingletonTest getInstance() {
return instance;
}
两种写法没有本质区别
懒汉模式写法
在需要使用该对象的时候再实例化对象
双重判断同步写法
为了线程安全需要使用synchronized关键字和volatile关键字
这里必须加volalite的原因:
new 对象的过程分为三步:
1.分配空间
2.初始化对象
3.指向对象内存地址。
2和3可能被编译器自动重排,导致判断非空但是实际拿的对象还未完成初始化
public class LazySingleton {
//加volatile关键字避免指令重排
private volatile static LazySingleton instance;
//解决高并发时创建多个对象 最简单的方法就是在方法上加个synchronized,然后方法里只需要做一次判断
//但是这是静态方法锁的就是这个class了,性能受影响,取而代之的是双重判断+同步代码块
//这里只需要锁判断对象是否创建就行了
public static /*synchronized*/ LazySingleton getInstance() {
if (instance == null) {
//优化的方法,使用同步代码块
synchronized (LazySingleton.class) {
//在多线程情况下需要判断,避免多实例
if (instance == null) {
instance = new LazySingleton();
// 1.分配空间 ->返回一个指向该空间的一个内存引用
// 2.对空间进行初始化
// 3.把内存引用复制给 变量
// 但是运行过程中2和3的顺序可能不对,出现指令重排,所以需要volatile关键字定义
}
}
}
return instance;
}
private LazySingleton() {}
}
有线程不安全懒汉式,线程安全懒汉式,同步代码块懒汉式,双重判断懒汉式
这里列举的是双重判断懒汉式
静态内部类写法
public class InnerSingleton {
//外部类在装载的时候静态内部类是不会被装载的
private static class Inner {
private final static InnerSingleton instance = new InnerSingleton();
}
//在此方法调用的时候才装载内部类,而且内部类只装载一次,线程安全
public static InnerSingleton getInstance() {
//调用此属性才会触发内部类的加载
return Inner.instance;
}
private InnerSingleton() {}
}
枚举类的写法
public class EnumForSingleton {
private EnumForSingleton() {}
public EnumForSingleton getInstance() {
return Singleton.INSTANCE.getInstance();
}
private enum Singleton {
/**
* 只写一个属性即可
* 用来获取单实例对象
* 这个枚举类本身也是单例的
*/
INSTANCE;
private EnumForSingleton singleton;
/**
* 枚举构造函数创建实例
* JVM这个方法只会调用一次
* 实例调用的时候才初始化
* 属于懒汉模式
*/
Singleton() {
singleton = new EnumForSingleton();
}
public EnumForSingleton getInstance() {
return singleton;
}
}
}
单例模式写法虽然有八种,但是能用的只有四种(饿汉的两种写法算一种)