zoukankan      html  css  js  c++  java
  • Java单例实现及分析

    双重检验锁实现方式

    public class Singleton {
        //定义一个私有的空构造方法,防止直接用new实例化
        private Singleton() {}
        
        private static volatile Singleton singleton =  null;
        
        public static Singleton getInstance() {
            if(singleton == null) {
                synchronized(Singleton.class) {
                    if(singleton == null) {
                        singleton = new Singleton();
                    }
                }
            }
            return singleton;
        }
    }

    双重校验锁,从代码的中可以看出,在同步代码块外多了一层instance为空的判断,由于单例对象只需要创建一次,如果后面再次调用getInstance()只需要直接返回单例对象即可,因此,在大部分情况下,调用getInstance()都不会执行到同步代码块,从而提高的程序性能。但是还需要考虑一种情况,假如两个线程A、B,线程A执行了if(instance == null)语句,它会任务单例对象没有创建,此时线程切到B也执行了同样的语句,B也认为单例对象没有创建,然后两个线程一次执行同步代码块,并分别创建了一个单例对象,所以为了解决这个问题,还需要在同步代码块中增加if(instance == null)的语句判断,避免重复创建的问题。

    可以发现instance变量使用了volatile修饰,这样做的目的在于禁止指令重排序优化,所谓指令重排序优化是指在不改变原语义的前提下,通过调整指令的执行顺序让程序运行的更快,JVM中并没有规定编译器优化的相关内容,也就是说JVM可以自由的镜像指令重排序优化,这样会导致Singleton和将对象赋值给instance字段的顺序是不确定的。在摸个线程创建单例对象时,在构造方法被调用之前,就为该对象分配了内存空间将对象的字段设置为默认值。此时就可以将分配的内存地址赋值给instance字段了,然而该对象可能还没有初始化,若紧接着另一个线程来调用getInsatance,取到的就是状态不正确的对象,程序就会出错。volatile的一个语义是禁止指令重排序优化,也就是保证了instance遍历被赋值的时候对象已经是初始化过的,从而避免上面说的问题。

    静态内部类实现方式

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

    这种方式同样利用了类加载机制来保证只创建一个instance实例。它与饿汉模式一样,也是利用了类加载机制,因此不存在多线程并发的问题。不一样的是,它是在内部类里面去创建对象实例。这样的话,只要应用中不使用内部类,JVM就不会去加载这个单例类,也就不会创建单例对象,从而实现懒汉式的延迟加载。也就是说这种方式可以同时保证延迟加载和线程安全。

  • 相关阅读:
    while循环
    No.四
    No. three
    第二章吧
    第二次写博客
    我人生的第一个程序,相当于哥伦布发现新大路。
    orale命令6 rman备份
    oracle 命令4 热备份
    oracle命令3 冷备份
    oracle命令2 和一致性关闭、非一致性关闭
  • 原文地址:https://www.cnblogs.com/conswin/p/10411446.html
Copyright © 2011-2022 走看看