Java:java学习笔记之Java单例模式的简单理解和使用

Posted JMW1407

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java:java学习笔记之Java单例模式的简单理解和使用相关的知识,希望对你有一定的参考价值。

Java单例模式的简单理解和使用

Java单例模式

1、饿汉式单例的实现如下:

//饿汉式实现
public class SingleB 
    private static final SingleB INSTANCE = new SingleB();
    private SingleB() 
    public static SingleB getInstance() 
        return INSTANCE;
    

2、懒汉式终极版本:volatile

// Version 4 
public class Single4 
    private static volatile Single4 instance;
    private Single4() 
    public static Single4 getInstance() 
        if (instance == null) 
            synchronized (Single4.class) 
                if (instance == null) 
                    instance = new Single4();
                
            
        
        return instance;
    


主要在于singleton = new Singleton()这句,这并非是一个原子操作,事实上在 JVM 中这句话大概做了下面 3 件事情:

  • 1、给 singleton 分配内存
  • 2、调用 Singleton 的构造函数来初始化成员变量,形成实例
  • 3、将singleton对象指向分配的内存空间(执行完这步 singleton才是非 null 了)

但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是 1-2-3 也可能是 1-3-2

  • 如果是后者,则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 instance 已经是非 null了(但却没有初始化),所以线程二会直接返回 instance,然后使用,然后顺理成章地报错。

再稍微解释一下,就是说,由于有一个『instance已经不为null但是仍没有完成初始化』的中间状态,而这个时候,如果有其他线程刚好运行到第一层if (instance == null)这里,这里读取到的instance已经不为null了,所以就直接把这个中间状态的instance拿去用了,就会产生问题。

  • 这里的关键在于——线程T1对instance的写操作没有完成,线程T2就执行了读操作

volatile关键字的作用

volatile关键字的一个作用是禁止指令重排,把instance声明为volatile之后,对它的写操作就会有一个内存屏障,这样,在它的赋值完成之前,就不用会调用读操作。

  • 注意:volatile阻止的不singleton = newSingleton()这句话内部[1-2-3]的指令重排,而是保证了在一个写操作([1-2-3])完成之前,不会调用读操作(if(instance == null))

3、Effective Java 1 —— 静态内部类

// Effective Java 第一版推荐写法
public class Singleton 
    private static class SingletonHolder 
        private static final Singleton INSTANCE = new Singleton();
    
    private Singleton ()
    public static final Singleton getInstance() 
        return SingletonHolder.INSTANCE;
    

这种写法非常巧妙:

  • 对于内部类SingletonHolder,它是一个饿汉式的单例实现,在SingletonHolder初始化的时候会由ClassLoader来保证同步,使INSTANCE是一个真·单例。
  • 同时,由于SingletonHolder是一个内部类,只在外部类的SingletongetInstance()中被使用,所以它被加载的时机也就是在getInstance()方法第一次被调用的时候。
  • 它利用了ClassLoader来保证了同步,同时又能让开发者控制类加载的时机。从内部看是一个饿汉式的单例,但是从外部看来,又的确是懒汉式的实现。

4、5.2 Effective Java 2 —— 枚举

// Effective Java 第二版推荐写法
public enum SingleInstance 
    INSTANCE;
    public void fun1()  
        // do something
    


// 使用
SingleInstance.INSTANCE.fun1();

参考

1、Hi,我们再来聊一聊Java的单例吧

以上是关于Java:java学习笔记之Java单例模式的简单理解和使用的主要内容,如果未能解决你的问题,请参考以下文章

Java:Effective java学习笔记之 用私有构造器或者枚举类型强化SIngleton属性

Java模式设计之单例模式(二)

Java设计模式学习笔记,一:单例模式

effective java笔记之单例模式与序列化

java笔记之静态修饰附和单例设计模式

Java:java学习笔记之Synchronized的简单理解和使用