设计模式(一)单例模式详解
一、单例模式
什么是单例模式?
单例模式是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。在Java中,单例模式通过以下几种方式实现:
-
将构造方法私有化,防止外部直接创建实例。
-
定义一个静态变量来保存唯一的实例。
-
提供一个公共的静态方法来返回这个实例。
单例模式的作用?
-
资源节约:确保系统中某个类只有一个实例,减少不必要的内存开销。
-
全局访问:提供一个全局访问点,方便在整个应用中访问同一个实例。
-
控制共享资源访问:确保对共享资源的访问是安全的且一致的。
为什么使用单例模式?
-
简化配置管理:对于需要频繁实例化然后销毁的对象,使用单例模式可以简化配置管理。
-
降低耦合度:通过单例模式,系统中的所有对象都可以通过一个统一的方式访问某个实例,从而降低类之间的耦合度。
-
避免多线程问题:多线程环境中,单例模式可以保证对象的唯一性,避免多线程同时操作同一个对象带来的问题。
单例模式的使用场景
-
日志文件管理:系统中通常只需要一个日志文件管理器来记录日志信息。
-
数据库连接池:为了提高性能,数据库连接池通常设计成单例模式,避免每次连接数据库时都要重新创建连接。
-
任务调度器:在一个系统中往往只需要一个任务调度器来管理任务的执行。
-
配置管理:应用程序的配置信息通常只需要加载一次,因为此配置管理类适合采用单例模式实现。
单例模式的分类
1、懒汉式1(线程不安全的)多线程环境下会出错。
懒加载:只有在第一次调用getInstance()方法时才会创建实例
java">package com.briup.singletonMyself;/*** 懒汉式单例1(线程不安全的)* @author 35329*/
public class LazySingleton1 {// 声明一个保存本类实例引用的静态变量private static LazySingleton1 instance;// 将构造方法私有化、防止其他类创建该类对象private LazySingleton1(){}// 提供一可以获取唯一实例的静态方法public static LazySingleton1 getInstance(){if (instance == null){instance = new LazySingleton1();}return instance;}
}
2、懒汉式2(线程安全)使用synchronized关键字保证线程安全,但每次调用getInstance()方法都会加锁,性能较低。
java">package com.briup.singletonMyself;/*** @author 35329** 懒汉式单例2(线程安全的)*/
public class LazySingleton2 {// instance: 实例private static LazySingleton2 instance;// 私有的构造器private LazySingleton2(){}// 专门创建一个静态性质的对象,充当同步锁private final static Object lock = new Object();// 提供一个方法,用于获取该实例public static LazySingleton2 getInstance(){synchronized (lock){if (instance == null){instance = new LazySingleton2();}}return instance;}
}
3、懒汉式3(使用synchronized和volatile修饰符确保多线程环境下的安全性,同时避免了每次调用getInstance()方法时都进行同步的性能开销)
java">package com.briup.singletonMyself;/*** @author 35329* 懒汉式单例3:* 1、对对象的创建和判断是否为空的操作 以原子性执行* 2、将类中的实例instance使用volatile修饰符修饰*/
public class LazySingleton3 {// instance : 静态变量,用来保存单例对象的唯一实例。// volatile修饰符:确保多线程环境下instance的可见性和有序性,// 防止指令重排导致的问题private volatile static LazySingleton3 instance;// 将构造方法私有化private LazySingleton3(){}// 使用静态对象作为同步锁,确保线程安全private final static Object lock = new Object();// 专门提供一个方法,用于获取该实例public static LazySingleton3 getInstance(){// synchronized(lock)确保在多线程环境下getInstance()方法内的代码块是同步执行的。synchronized (lock){// 只有确保在instance为null时才会创建新的实例if (instance == null){instance = new LazySingleton3();}}return instance;}
}
4、懒汉式+线程同步+可见性+DCL(双重锁验证机制)
java">package com.briup.singletonMyself;/*** @author 35329* 单例模式:懒汉式+线程同步+可见性+DCL* DCL:Double Check Lock,双重锁验证机制*/
public class DCLSingleton {// instance 实例private volatile static DCLSingleton instance;// 私有构造器private DCLSingleton(){}// 同步锁 确保线程安全private static final Object lock = new Object();// 提供一个方法,用于获取该实例public static DCLSingleton getInstance(){if (instance == null){// synchronized (DCLSingleton.class) 这两句相同 详细解析看下方注释synchronized (lock){if (instance == null){instance = new DCLSingleton();}}}return instance;}/** synchronized (DCLSingleton.class) 是DCLSingleton类的对象引用* 类对象锁是针对类对象的锁,可以确保在类加载时只有一个线程能够进入同步块* * synchronized(lock) 是一个普通的对象引用,对象锁是针对特定对象的锁,* 可以确保在多线程的环境下只有一个线程能够进入同步块。* 由于lock是静态对象,因此所有对DCLSingleton类的实例化操作都会受到这个锁的影响。* 这种方式可以确保在多线程环境下,只有一个线程能够进入同步块并创建实例。* * 这两种方式都能保证线程安全,但synchronized(lock)更常用,因为它不会影响* 类的其他操作。而synchronized(XXXXX.class)虽然也能保证线程安全,但可能会影响类的其他操作。* 具体选择那种方式取决于实际需求和性能考虑。*/
}
5、饿汉式
java">package com.briup.singletonMyself;/*** 饿汉式单例:不使用显示初始化,而是使用static{}对实例进行创建*/
public class HungrySingleton {// 创建一个Hungry类的唯一实例private final static HungrySingleton INSTANCE;static {INSTANCE = new HungrySingleton();}// 私有构造函数,防止外部实例化private HungrySingleton(){}// 提供一个公共的静态方法,用于返回唯一的实例public static HungrySingleton getInstance(){return INSTANCE;}
}
6、静态内部类
java">package com.briup.singletonMyself;/*** @author 35329* 单例模式:借助静态内部类实现*/
public class StaticInnerSingleton {// 将构造器私有化,防止外界创建该类对象private StaticInnerSingleton(){}/*** InstanceHolder是一个静态内部类,它持有StaticInnerSingleton的唯一实例** INSTANCE:是一个静态最终变量,用于保存StaticInnerSingleton的唯一实例** 静态内部类InstanceHolder只会在首次访问getInstance()方法时被加载,* 此时INSTANCE会被初始化。由于类加载是线程安全的,因此这种方法保证了线程的安全*/private static class InstanceHolder{ // 外部类实例的持有者// 将外部类的唯一实例声明在这里private static final StaticInnerSingleton INSTANCE = new StaticInnerSingleton();}// 提供了一个静态方法用于获取唯一的实例// 懒加载:只有在首次调用getInstance()方法时才会创建实例,之后每次调用都会返回已创建的实例public static StaticInnerSingleton getInstance(){return InstanceHolder.INSTANCE;}}
7、枚举
java">package com.briup.singletonMyself;/*** @author 35329* * 单例模式:枚举实现* 过早的将对象创建出来加载到内存,但是线程是安全的,简洁,推荐使用*/
public enum EnumSingleton {INSTANCE; // 唯一的实例// 定义一个静态方法 用来返回该实例的方法public static EnumSingleton getInstance(){return INSTANCE;}
}
后续设计模式等待更新中...