深入谈谈Java最简单的单例设计模式

  单例设计模式是23种设计模式里面最简单的,但是要彻底理解单例,还是需要下一点功夫的。

单例一般会分为饿汉模式和懒汉模式

饿汉模式:

1 public class Singleton
2 {
3     private static Singleton singleton = new Singleton();
4
5     public static Singleton getInstance()
6     {
7         return singleton;
8     }
9 }

但是在一些系统应用环境中,这个单例对象可能比较大,在类加载的时候就初始化对象会增加系统启动压力,还会对系统资源造成浪费。所以就有了懒汉模式,只有在第一次调用的时候才创界对象实例。

懒汉模式:

 1 public class Singleton
 2 {
 3     private static Singleton singleton;
 4
 5     public static Singleton getInstance()
 6     {
 7         if (singleton == null)
 8         {
 9             singleton = new Singleton();
10         }
11         return singleton;
12     }
13 }

但是在多线程的环境中,以上内容就会有问题了。由于没有同步,不同的线程可能同时进入if语句,然后分别创建了两个实例,这时候Singleton就不再是单列了。

于是就有了双重空判断版本

public class Singleton
{
    private static Singleton1 singleton;

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

网上很多资料说,这种写法也不是线程安全的,singleton字段必须定义为volatile才行。但实际上,以上代码其实并没有线程安全问题,因为Singleton1这个类并没有状态量。举个例子,以下代码才是非线程安全的:

public class WrongSingleton
{
    private static WrongSingleton singleton;

    public Object state = new Object();

    public static WrongSingleton getInstance()
    {
        if (singleton == null)
        {
            synchronized (WrongSingleton.class)
            {
                if (singleton == null)
                    singleton = new WrongSingleton();
            }
        }
        return singleton;
    }
}

由于cpu指令重排序的存在,我们无法确认singleton对象引用和state对象引用回写到内存的顺序,如果Singleton对象的引用已经由线程A回写到了内存,而对象内部持有的state字段还未完成回写,那么此时线程B调用getInstance()方法后,将得到一个错误的Singleton对象。其state引用为null。

要解决这个问题有两个办法:

一、把 private static WrongSingleton singleton; 改为  private static volatile Singleton singleton; 由于volatile 的可见性语意,所有对volatile变量的修改,都会立即回写到主存,所以在Singleton构造函数返回前,state对象就已经回写到主存了。

二、把state 对象定义为final字段。由于final语意,final对象在构造函数完成后,其值的可见性对所有线程保持一致。

时间: 2024-10-02 23:59:33

深入谈谈Java最简单的单例设计模式的相关文章

Java基础小常识- 单例设计模式,-(9)

一.单例设计模式 1.定义:就是可以保证一个类在内存中的对象唯一性,单例设计模式有两种方式,一种饿汉式(实际开发中用),一种懒汉式(面试中用) 必须对于多个程序使用同一个配置信息对象时,就需要保证该对象的唯一性. 2.如何保证对象唯一性 1)不允许其他程序用new创建该类对象 2)在该类创建一个对象实例 3)对外提供一个方法让其他程序可以过去该对象 3.实现步骤 1)私有化该类构造函数 2)通过new在本类中创建一个本类对象 3)定义一个公有的方法,将创建的对象返回 4.实例演示 //饿汉式,类

Java单例设计模式的实现

1. 单例设计模式的定义 单例设计模式确保类只有一个实例对象,类本身负责创建自己的对象并向整个系统提供这个实例.在访问这个对象的时候,访问者可以直接获取到这个唯一对象而不必由访问者进行实例化. 单例设计模式保证了全局对象的唯一性,在许多场景中都有应用.例如Windows中多个进程或线程同时操作一个文件的时候,所有的进程或线程对于同一个文件的处理必须通过唯一的实例来进行. 2. java单例设计模式的几种实现方式 单例设计的最大特点是类的构造函数是私有的,确保了只能由类本身创建对象,而访问者无法进

实现单例设计模式的多种方式

Singleton:在Java中即指单例设计模式,它是软件开发中最常用的设计模式之一. 单例设计模式,即即某个类在整个系统中只能有一个实例对象可被获取和使用的代码模式. 例如:代表JVM运行环境的Runtime类 要点: - 某个类只能有一个实例:构造器私有化 - 它必须能够创建该类实例:使用该类的静态变量来保存这个唯一的实例. - 它必须能返回该实例:通过get方法返回该实例或直接暴露给外部. 一. 饿汉式:直接创建对象,不存在线程安全问题 1.1. 直接实例化饿汉式(简洁直观) /* * 饿

如何创建单例设计模式

单例设计模式应用场景:比如一个播放器一次只播放一首歌 class MusicPlayer(object): # 记录第一个被创建对象的引用 instance = None # 记录是否执行过初始化的动作 init_flag = False # 改造new方法 def __new__(cls, *args, **kwargs): # 1.判断类属性是否为空 if cls.instance is None: # 2.调用父类方法,为第一个对象分配空间 cls.instance=super().__n

黑马程序员--Java基础学习笔记【单例设计模式、网络编程、反射】

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 设计模式分类: 创建模式:是对类的实例化过程的抽象化,又分为类创建模式和对象创建模式 类创建模式:工厂方法模式 对象-:简单工厂(静态工厂方法)模式.抽象工厂模式.单例模式.建造模式- 结构模式:描述如何将类或者对象结合在一起形成更大的结构 适配器模式.缺省模式.合成模式.装饰模式(包装模式).门面模式- 行为模式:对不同的对象之间划分责任和算法的抽象化 不变模式.策略模式.迭代子模式.命令模

OC学习-单例设计模式的理解、案例和简单总结

单例模式,就是一个类始终只有一个实例,不管如果copy还是retain还是alloc等等,都只有一个实例.为什么?有什么好处? 简单来说: a:有的东西只能有一个,那就必须用单例: b:单例的好处就是不会有多余的实例,所以节约内存: c:因为只有一个单例,所以易于管理多线程对它的访问. d:其他的原因……省略 我们创建一个单例的User类,然后生成一个user1对象,再把这个对象进行copy.retain这些,再看看它们是否是同一个实例,还是被创建出多个实例? (1)User.h view so

java设计模式_single(单例设计模式)

设计模式:解决某一类问题最行之有效的方法,java中有23种设计模式 一.单例设计模式概述: 1.解决一个类在内存中只有一个对象(保证一个类仅有一个实例,并提供一个访问他的全局访问点)  2.要保证对象的唯一: 1.为了避免其他程序过多的建立该类对象,先禁制其他程序建立该类对象 2.为了让其他程序可以访问到该类对象,只好在本类中,自定义一个对象 3.为了 方便其他程序对自定义对象的访问,可以对外提供一些访问方式 3.代码实现步骤: 1.将构造函数私有化 2.在类中创建一个本类对象 3.给外部提供

【OC学习-29】单例设计模式的理解、案例和简单总结

单例模式,就是一个类始终只有一个实例,不管如果copy还是retain还是alloc等等,都只有一个实例.为什么?有什么好处? 简单来说: a:有的东西只能有一个,那就必须用单例: b:单例的好处就是不会有多余的实例,所以节约内存: c:因为只有一个单例,所以易于管理多线程对它的访问. d:其他的原因--省略 我们创建一个单例的User类,然后生成一个user1对象,再把这个对象进行copy.retain这些,再看看它们是否是同一个实例,还是被创建出多个实例? (1)User.h #import

JAVA之旅(十四)——静态同步函数的锁是class对象,多线程的单例设计模式,死锁,线程中的通讯以及通讯所带来的安全隐患,等待唤醒机制

JAVA之旅(十四)--静态同步函数的锁是class对象,多线程的单例设计模式,死锁,线程中的通讯以及通讯所带来的安全隐患,等待唤醒机制 JAVA之旅,一路有你,加油! 一.静态同步函数的锁是class对象 我们在上节验证了同步函数的锁是this,但是对于静态同步函数,你又知道多少呢? 我们做一个这样的小实验,我们给show方法加上static关键字去修饰 private static synchronized void show() { if (tick > 0) { try { Thread