- /**
- * 完整的懒汉式
- *
- */
- public class SingletonFive {
- private SingletonFive() {
- init();
- }
- private void init() {
- user = new User();
- }
- private User user;
- private static class LazyHolder {
- private static final SingletonFive INSTANCE = new SingletonFive();
- }
- public static SingletonFive getInstance(){
- return LazyHolder.INSTANCE;
- }
- public User getUser(){
- return user;
- }
- }
double check 懒汉模式 (DCL)
- public class Singleton{
-
- private static Singleton instance;
-
- private Singleton(){
- }
-
- public static Singleton getInstance(){
-
- if(instance==null){
- synchronized(Singleton.class){
- if(instance==null){
- instance=new Singleton();
- }
- }
- }
- return instance;
-
缺点:避免的上面方式的明显缺点,但是java内存模型(jmm)并不限制处理器重排序,在执行instance=new Singleton();时,并不是原子语句,实际是包括了下面三大步骤:
1.为对象分配内存
2.初始化实例对象
3.把引用instance指向分配的内存空间
这个三个步骤并不能保证按序执行,处理器会进行指令重排序优化,存在这样的情况:
优化重排后执行顺序为:1,3,2, 这样在线程1执行到3时,instance已经不为null了,线程2此时判断instance!=null,则直接返回instance引用,但现在实例对象还没有初始化完毕,此时线程2使用instance可能会造成程序崩溃。
现在要解决的问题就是怎样限制处理器进行指令优化重排
- //使用volatile,double check单例模式
- public class Singleton{
-
- private static volatile Singleton instance;
-
- private Singleton(){
- }
-
- public static Singleton getInstance(){
-
- if(instance==null){
- synchronized(Singleton.class){
- if(instance==null){
- instance=new Singleton();
- }
- }
- }
- return instance;
- }
- }
- 这里就要介绍一下volatile的作用了:
1.保证可见性
可以保证在多线程环境下,变量的修改可见性。每个线程都会在工作内存(类似于寄存器和高速缓存),实例对象都存放在主内存中,在每个线程要使用的时候把主内存中的内容拷贝到线程的工作内存中。使用volatile关键字修饰后的变量,保证每次修改了变量需要立即写回主内存中,同时通知所有的该对变量的缓存失效,保证缓存一致性,其他线程需要使用该共享变量时就要重新从住内存中获取最新的内容拷贝到工作内存中供处理器使用。这样就可以保证变量修改的可见性了。但volatile不能保证原子性,比如++操作。
2.提供内存屏障
volatile关键字能够通过提供内存屏障,来保证某些指令顺序处理器不能够优化重排,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。
下面是保守策略插入内存屏障:
- 在每个volatile写操作的前面插入一个StoreStore屏障。
- 在每个volatile写操作的后面插入一个StoreLoad屏障。
- 在每个volatile读操作的前面插入一个LoadLoad屏障。
- 在每个volatile读操作的后面插入一个LoadLoad屏障。
这样可以保证在volatile关键字修饰的变量的赋值和读取操作前后两边的大的顺序不会改变,在内存屏障前面的顺序可以交换,屏障后面的也可以换序,但是不能跨越内存屏障重排执行顺序。
好了,现在来看上面的单例模式,这样就可以保证3步骤(instance赋值操作)是保持最后一步完成,这样就不会出现instance在对象没有初始化时就不为null的情况了。这样也就实现了正确的单例模式了。
分享到:
相关推荐
单例模式常用于控制共享资源,如线程池、数据库连接或者配置对象。其优点在于节省内存,减少对系统资源的消耗,同时保证了对象间的同步。 **工厂模式**是一种创建型设计模式,它提供了一种创建对象的最佳方式。在...
单例模式是一种设计模式,旨在确保一个类只有一个实例,并提供全局访问点。在单例模式中,类的构造函数是私有的,防止外部直接创建对象,而是通过静态方法获取该类的唯一实例。单例模式的唯一性通常是在进程范围内,...
在Java等面向对象编程语言中,单例模式常用于管理共享资源,如数据库连接池、线程池或者配置文件等。结合工厂模式,可以进一步优化单例的创建过程,提高代码的可读性和可维护性。 单例模式的核心在于控制类的实例化...
在C#中,单例模式常用于管理共享资源或控制类的实例化过程,以提高性能、节约系统资源,特别是在整个应用程序生命周期内只需要一个对象的情况下。 首先,实现C#单例模式通常有几种常见方法: 1. 饿汉式(静态常量...
单例模式是软件设计模式中的一种经典模式,用于确保一个类只有一个实例,并提供一个全局访问点。...通过学习和实践这些单例模式的实现,我们可以更好地理解和应用设计模式,提升代码的质量和可维护性。
在C++编程中,单例模式是一种常用的软件设计模式,它保证一个类只有一个实例,并提供一个全局访问点。线程安全的单例模式在多线程环境下尤其重要,因为不正确的实现可能导致多个线程创建多个实例,这违反了单例模式...
这种模式常用于控制共享资源,如数据库连接池、线程池或者日志系统等,确保这些资源在整个应用程序中只被创建一次。 单例模式的核心思想是限制类的实例化,使得无论通过何种方式,都只能获取到同一个对象。实现单例...
在软件工程中,单例模式常用于控制资源的共享,比如数据库连接池、线程池或者日志系统等,这些资源通常需要全局唯一且高效地访问。 在Java中,实现单例模式有多种方式,但最常见的问题是线程安全问题。例如,上述...
Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式...
单例模式是软件设计模式中的一种经典模式,其主要目的是确保一个类只有一个实例,并提供一个全局访问点。这种模式在很多场景下非常有用,比如控制共享资源、管理配置对象等。下面将详细介绍七种常见的单例模式实现...
在Java或类似的面向对象编程语言中,单例模式常用于管理共享资源,如数据库连接池、线程池或者配置文件等。在这个场景中,我们将探讨如何使用单例模式来创建一个学生管理系统,主要涉及“饿汉式”和“懒汉式”两种...
在Java编程中,单例模式常用于控制资源的访问,比如数据库连接池、线程池或者日志对象等。本篇文章将深入探讨如何在Java中实现单例模式,以及如何创建一个工具类来封装单例的创建。 首先,让我们了解单例模式的几种...
其中,单例模式是一种非常经典且常用的设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。在C#中,我们可以利用单例模式来创建一个计数器类,以确保在整个应用程序的生命周期内,计数器只...
在C++编程中,单例模式是一种常用的软件设计模式,它保证一个类只有一个实例,并提供一个全局访问点。在这个特定的场景中,我们讨论的是一个实现了单例模式的日志类,该类专为多线程环境设计,具备日志等级控制、...
单例模式是软件设计模式中的一种,它的主要目的是确保一个类只有一个实例,并提供一个全局访问点。这种模式在很多场景下都非常有用,比如控制资源的唯一性、管理共享配置或者创建昂贵的对象时避免频繁创建销毁。 ...
单例模式常用于控制资源的共享,例如数据库连接池、日志服务等。单例模式有多种实现方式,常见的包括懒汉式、饿汉式以及双重检查锁定(DCL)等。 1. 懒汉式:懒汉式是在第一次使用时才创建单例对象,以延迟加载提高...
单例模式是软件设计模式中的经典模式之一,其主要目的是控制类的实例化过程,确保在应用...在设计模式中,单例模式不仅限于上述实现,还可以与其他模式结合使用,如工厂模式、装饰器模式等,以达到更灵活、高效的设计。
### 单例模式应用场景 #### 一、概述 在软件工程领域中,设计模式作为一种解决常见问题的方法,被广泛应用于各种编程环境中。其中,单例模式(Singleton Pattern)作为最简单的设计模式之一,旨在确保一个类只有一...
单例模式常用于以下场景: * 资源管理器,例如打印机驱动程序对象、缓存、日志对象等。 * 全局变量,例如 JavaWeb 中的 application 对象。 * 需要避免不一致状态的场景,例如 Printer Spooler、通信端口等。 单例...