java 单例模式使用与整理

单例模式详解
本文深入解析单例模式,探讨其在Java中的应用,包括模式定义、优缺点及五种实现方式,如懒汉式、饿汉式和静态内部类方式。

单例模式是什么?

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

我们为什么要使用单例模式?

它可以控制实例数目,节省系统资源。

优点: 

1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。

2、避免对资源的多重占用(比如写文件操作)。

缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

一般使用场景: 

1、要求生产唯一序列号。

2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。

3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。

 

单例模式实现方式,基本上就五种:

1)标准原始的单例模式的构造方式

public class Singleton {
    private static Singleton instance=null;
    private Singleton(){
        
    }
    public static Singleton getInstance(){
        if(instance==null){
            instance=new Singleton();
        }
        return instance;
    }
}

PS:Singleton的静态属性instance中,只有instance为null的时候才创建一个实例,构造函数私有,确保每次都只创建一个,避免重复创建。
缺点:只在单线程(只被一个地方使用)的情况下正常运行,在多线程(同时多个地方使用)的情况下,就会出问题(如内存泄漏)。例如:当两个线程同时运行到判断instance是否为空的if语句,并且instance确实没有创建好时,那么两个线程都会创建一个实例。

因为上述的缺点,所以机智程序员,又想出一个可以多线程使用的方式解决

2)俗称懒汉式

public class Singleton {
    private static Singleton instance=null;
    private Singleton(){
        
    }
    public static synchronized Singleton getInstance(){
        if(instance==null){
            instance=new Singleton();
        }
        return instance;
    }
}

PS:在解法一的基础上加上了同步锁,使得在多线程的情况下可以用。

缺点:上面的做法很简单,就是将整个获取实例的方法同步,这样在一个线程访问这个方法时,其它所有的线程都要处于挂起等待状态,倒是避免了刚才同步访问创造出多个实例的危险,只能说,这样的处理方式也是是够糟的,因为这样会造成很多无谓的等待。

因为上述的缺点,所以机智程序员,又想出一个避免时间等待的方法

3)双重锁方式

public class Singleton {
    private static Singleton instance=null;
    private Singleton(){
        
    }
    public static Singleton getInstance(){
        if(instance==null){
            synchronized(Singleton.class){
                if(instance==null){
                    instance=new Singleton();
                }
            }
        }
        return instance;
    }
}

PS:这种做法与上面那种粗暴的同步做法相比就要好很多了,因为我们只是在当前实例为null,也就是实例还未创建时才进行同步,否则就直接返回,这样就节省了很多无谓的线程等待时间,值得注意的是在同步块中,我们再次判断了Singleton是否为null

缺点:用双重if判断,复杂,容易出错。

4)俗称饿汉式

public class Singleton {
    private static Singleton instance=new Singleton();
    private Singleton(){
        
    }
    public static Singleton getInstance(){
        return instance;
    }
}

PS:初试化静态的instance创建一次。如果我们在Singleton类里面写一个静态的方法不需要创建实例,它仍然会早早的创建一次实例。而降低内存的使用率。

缺点:这种方式最主要的缺点就是一旦我访问了Singleton的任何其他的静态域,就会造成实例的初始化,而事实是可能我们从始至终就没有使用这个实例,造成内存的浪费。

 不过在有些时候,直接初始化单例的实例也无伤大雅,对项目几乎没什么影响,比如我们在应用启动时就需要加载的配置文件等,就可以采取这种方式去保证单例。

鉴于上述方式还是有点缺陷,因此继续出现一种解决方案

5)静态内部类方式

public class Singleton {
    private Singleton(){
        
    }
    private static class SingletonHolder{
        private final static Singleton instance=new Singleton();
    }
    public static Singleton getInstance(){
        return SingletonHolder.instance;
    }
}

PS:

 好了,进行到这里,单例模式的自我优化之路算是完成了。最终的产物就是如上述的形式。上述形式保证了以下几点。

  1.Singleton最多只有一个实例,在不考虑反射强行突破访问限制的情况下。

  2.保证了并发访问的情况下,不会发生由于并发而产生多个实例。

  3.保证了并发访问的情况下,不会由于初始化动作未完全完成而造成使用了尚未正确初始化的实例。

<think>我们正在讨论Java单例模式的应用场景和作用。根据用户需求,需要整理单例模式的用途和应用场景,并参考提供的引用内容。 引用[1]提到单例模式用于确保一个类只有一个实,并提供一个全局访问点。 引用[2]讨论了不同单例模式实现方式的选择,根据延迟加载、线程安全等需求。 引用[3]详细介绍了单例模式的应用场景、优缺点以及各种实现方式(包括线程安全)。 引用[4]给出了饿汉式的代码示。 因此,回答应包含: 1. 单例模式的定义(确保一个类只有一个实,全局访问点)。 2. 单例模式的用途(为什么需要)。 3. 单例模式的应用场景(具体在哪些情况下使用)。 4. 可以简要提及实现方式(但重点在应用场景和用途)。 注意:用户的问题集中在“应用场景和作用”,所以重点在这两方面。 结构: 一、单例模式的作用(用途) 二、单例模式的应用场景 三、实现方式的选择建议(根据引用[2]和[3]的内容,简要说明不同场景下如何选择实现方式) 同时,按照要求生成相关问题。 注意:引用标识自然添加在相关内容的末尾。</think>### Java 单例模式的用途应用场景详解 #### **一、单例模式的核心作用** 单例模式的核心是**确保一个类仅有一个实**,并提供全局访问点[^1][^4]。主要用途包括: 1. **资源统一管理** 避免重复创建资源密集型对象(如数据库连接池、线程池),减少系统开销[^3]。 > *示:数据库连接池* ```java public class DatabasePool { private static DatabasePool instance; private List<Connection> connections; // 连接池资源 private DatabasePool() { /* 初始化连接池 */ } public static synchronized DatabasePool getInstance() { if (instance == null) instance = new DatabasePool(); return instance; } } ``` 2. **数据一致性保障** 全局唯一实可避免多线程环境下数据状态冲突,如配置管理、计数器服务[^3]。 3. **减少内存开销** 通过复用实,降低 JVM 堆内存占用(尤其适用于频繁创建销毁的对象)[^1]。 --- #### **二、典型应用场景** 根据引用内容[^2][^3],单例模式适用于以下场景: 1. **配置管理类** - 场景:读取应用配置文件(如 `app.properties`)。 - 优势:避免多次加载配置文件导致数据不一致[^3]。 ```java public class ConfigManager { private static final ConfigManager instance = new ConfigManager(); // 饿汉式 private Properties props; private ConfigManager() { loadConfig(); } public static ConfigManager getInstance() { return instance; } } ``` 2. **日志记录器** - 场景:统一处理日志写入(文件/数据库)。 - 优势:防止多线程日志写入冲突[^3]。 3. **硬件交互接口** - 场景:打印机服务、串口通信等独占资源访问。 - 优势:确保硬件资源被一控制[^1]。 4. **缓存管理器** - 场景:实现 LRU 缓存(如 `LinkedHashMap` 扩展)。 - 优势:通过控制缓存淘汰策略[^1][^3]。 5. **线程池/连接池** - 场景:数据库连接池(如 HikariCP)、线程池(如 `ExecutorService`)。 - 优势:避免连接泄漏和资源竞争[^3]。 --- #### **三、实现方式选择建议** 根据引用[^2][^3][^4],不同场景下的实现方式选择: | **场景需求** | **推荐实现** | **原因** | |----------------------------|-----------------------|---------------------------------------------| | 要求启动速度,且实轻量 | 饿汉式(Eager) | 类加载时初始化,无锁开销[^4] | | 延迟加载 + 高并发 | 静态内部类 | 懒加载 + JVM 保证线程安全(无同步锁)[^2] | | 需防序列化/反射破坏 | 枚举 | 天然防序列化和反射攻击[^3] | | 需灵活控制初始化时机 | 双重检查锁(DCL) | 懒加载 + 细粒度锁(JDK 5+)[^2] | > **代码示:静态内部类实现(线程安全 + 懒加载)** > ```java > public class Logger { > private Logger() {} // 私有构造 > private static class Holder { > private static final Logger INSTANCE = new Logger(); > } > public static Logger getInstance() { > return Holder.INSTANCE; // 首次调用时加载内部类 > } > } > ``` --- ### **相关问题** 1. 如何防止单例模式被反射破坏? 2. 枚举相比双重检查锁有哪些优势? 3. 单例模式在 Spring 框架中是如何应用的? 4. 单例模式如何影响元测试?如何解决? > 参考资料: > [单例模式深度解析](https://example.com/singleton-pattern) [^3] | [Java 设计模式实战](https://example.com/java-design-patterns) [^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值