保证系统内存只有一个对象,节省系统资源,对于需要频繁创建销毁的对象,使用单例模式可以提高性能
提供一个获取对象的方法,而不是直接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;}}}
单例模式写法虽然有八种,但是能用的只有四种(饿汉的两种写法算一种)
