设计模式(一)单例模式详解
一、单例模式
什么是单例模式?
单例模式是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。在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;
}
}
++后续设计模式等待更新中...++