/* 单例设计模式:是一种创建型设计,让你能够保证一个类只有一个实例,并提供一个 实例的全局节点。 问题: 单例模式解决了两个问题: 1.保证一个类只有一个实例 2.为该实例提供一个全局访问节点 单例模式允许保护该实例不被其他代码覆盖。 但是单例模式 违反了 单一职责原则 单一职责原则:类的职责单一,对外值提供一种功能,而引起类变化的原则只有一个。 饿汉式:类加载就会导致该单实例对象被创建。 懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象的时候才创建。 */
①.饿汉式
饿汉式的优缺点: 缺点:对象的创建是在静态代码块中,也是对类的加载而创建。存在内存的浪费。 优点: 线程安全。 第一种实现: static class Singleton_Hungry{ //1.私有 静态成员变量 来保存 该类的对象 private static Singleton_Hungry singleton_hungry=new Singleton_Hungry(); // 2.私有构造方法 private Singleton_Hungry() { } //3. 提供一种 静态的方法 获取该对象 public static Singleton_Hungry getInstance(){ return singleton_hungry; } } public static void main(String[] args) { Singleton_Hungry singleton_hungry=Singleton_Hungry.getInstance(); Singleton_Hungry singleton_hungry1=Singleton_Hungry.getInstance(); System.out.println(singleton_hungry==singleton_hungry1);// true } 第二种实现: static class Singleton_Hungry{ private static Singleton_Hungry singleton_hungry; //在静态代码块里面赋值 static { singleton_hungry=new Singleton_Hungry(); } //私有构造方法 private Singleton_Hungry(){ } // 对外提供 获取该类对象的方法 //实例 public static Singleton_Hungry getInstance(){ return singleton_hungry; } } public static void main(String[] args) { Singleton_Hungry instance1 = Singleton_Hungry.getInstance(); Singleton_Hungry instance2 = Singleton_Hungry.getInstance(); System.out.println(instance1==instance2); }
② 懒汉式 第三种实现: 懒汉式: 第一种: 线程不安全,但是在 getInstance() 方法上添加了synchronized关键字,, 导致该方法的执行效果特别低。 从上面代码可以看出,其实就是在初始化instance的时候出现安全问题, 一旦初始化完成就不存在了。
static class Singleton_Lazy{ private static Singleton_Lazy singleton_lazy; private Singleton_Lazy(){ } public static synchronized Singleton_Lazy getInstance(){ if(singleton_lazy==null){ // 线程1 走到这里,等待 // 线程2获取到cup 的执行权 ,也会进入到该判断里面 // 那么 我们创建 的就不是 单线程了 singleton_lazy=new Singleton_Lazy(); } return singleton_lazy; } } public static void main(String[] args) { Singleton_Lazy singleton_lazy1=Singleton_Lazy.getInstance(); Singleton_Lazy singleton_lazy2=Singleton_Lazy.getInstance(); System.out.println(singleton_lazy1==singleton_lazy2); }
双重校验锁:
第四种实现:
/** * 我们在讨论一下懒汉式 中加锁的问题, 对于getInstance() 方法来说,绝大数的操作都是读操作, * 读操作是线程安全的,所以我们没有必要让每个线程必须 持有锁才能 调用,我们需要 * 调整加锁的时机。由此也产生了一种新的实现模式:双重校验锁模式。 * */ /* 双重校验锁模式是一种非常好的单例模式,解决了单例、性能、线程安全问题 上面的双重校验检测锁模式看上去完美无缺, 其实是存在问题的,在 多线程的情况下,可能会出现空指针问题, 出现问题的原因是JVM 在实例化对象的时候 会出现 优化和指令重排操作 要解决双重检验锁模式带来的空指针异常的问题,只需使用 volatile关键字, volatile 关键字可以保证可见性和有序性。 不可见性: 并发编程下,多线程修改变量,会出现线程间 变量的不可见性 变量不可见性内存语义: JMM(java memory model) java 内存模型 */ static class Singleton_Lazy{ //成员变量 private static volatile Singleton_Lazy singleton_lazy; //私有的构造器 private Singleton_Lazy(){ } public static Singleton_Lazy getInstance(){ // 第一判断, 如果 singleton_lazy 不为空 不需要抢占锁,直接返回对象 if(singleton_lazy==null){ synchronized (Singleton_Lazy.class){ if(singleton_lazy==null){ singleton_lazy=new Singleton_Lazy(); } } } return singleton_lazy; } }
public static void main(String[] args) { Singleton_Lazy instance1 = Singleton_Lazy.getInstance(); Singleton_Lazy instance2 = Singleton_Lazy.getInstance(); System.out.println(instance1==instance2); }
第五种实现 :静态内部类
/* 静态内部类 单例模式中 实例由 内部类创建, 由于jvm 在加载外部类的过程中,是不会加载内部类的属性和方法 被调用的才会被加载,并初始化其静态属性。静态 属性由于被static 修饰 保证只被实例化一次,并且严格保证实例化顺序。 */ static class Singleton_Lazy{ // 私有 构造器 private Singleton_Lazy(){ } // 定义 一个静态内部类 private static class Singleton_LazyHolder{ // 在 内部类 中 声明 并初始化外部类对象 private static final Singleton_Lazy SINGLETON_LAZY=new Singleton_Lazy(); } // 提供 公共的访问的方式 public static Singleton_Lazy getInstance(){ return Singleton_LazyHolder.SINGLETON_LAZY; } } public static void main(String[] args) { Singleton_Lazy instance1 = Singleton_Lazy.getInstance(); Singleton_Lazy instance2 = Singleton_Lazy.getInstance(); System.out.println(instance1==instance2); }
第六种:枚举方式
/* 枚举类实现单例模式是极力推荐的单例实现模式,因为枚举是线程安全的 并且只会装载一次,设计者充分利用了枚举的这个特性来实现单例模式, 枚举的写法非常简单,而且枚举类型是所用单例实现唯一一种不会破坏的单例实现模式 */
// 枚举 属于 恶汉式 enum Singleton{ INTERFACE; } public static void main(String[] args) { Singleton singleton1=Singleton.INTERFACE; Singleton singleton2=Singleton.INTERFACE; System.out.println(singleton2==singleton1); }