java中的单例模式

Posted zxrxzw

tags:

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

1.饿汉模式:

public class Singleton {
    private Singleton() {
        // 必须是私有构造方法
    }

    private static Singleton instance = new Singleton();

    public static Singleton getInstance() {
        return instance;
    }
}

  因为饿汉模式在调用类的时候就new对象,这样浪费空间,所以考虑这种情况,有了懒汉模式

 

2.懒汉模式:

class Singleton {
    private Singleton() {
        //must
    }

    private static Singleton instance = null;

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

  在多线程的情况下,饿汉模式的判断对象是否为空语句会出现冲突,所以,再添加了是否线程安全的考虑

 

3.懒汉模式,考虑线程安全:

public class Singleton {
    private Singleton() {
        //must
    }

    private static Singleton instance = null;

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

  在方法前面加了一个synchronized这样的关键字,给此方法加了同步锁。但是为了缩小锁的范围使得效率更高,就考虑到了双重检查锁。

 

4.双重检查锁:又叫做   双重检验锁模式(double checked locking pattern

 

public class Singleton {
    private Singleton() {
        //must
    }

    private static Singleton instance = null;

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

  上面的代码其实就是大名鼎鼎的DCK了。这个大大提高了并发访问性能,而且实现了延迟初始化。如果第一个 instance 不为 null 的话,就可以直接返回了,减少了同步开销。只有当 instance null 的时候,才会进行同步操作。这个时候需要在进行一次 instance 是否为 null 的检查。因为,有可能两个线程都判断 instance null,一个线程加锁,对instance进行了实例化,释放锁,另外一个线程拿到锁后,不进行 instance 是否为 null 的再次判断,会再次进行instance的实例化。 

  但是上面的代码还是有一块需要考虑,就是在new对象的时候其实java虚拟机是分成三步来操作的,

new 字节码对应的处理流程:

 

1instance 分配内存

 

2调用 Singleton 的构造函数来初始化成员变量(invokespecial

 

3instance对象指向分配的内存空间(执行完这步 instance 就为非 null 了)

 

但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者,则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 instance 已经是非 null 了(但却没有初始化),所以线程二会直接返回 instance,然后使用,然后顺理成章地报错。所以,现在有一个关键字出现来解决这个问题。

 

5.volatile 实现

 

public class Singleton {
    private Singleton() {
        //must
    }

    private static volatile Singleton instance = null;

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

JDK1.5 及之后版本增加了 volatile 关键字的一个语义:禁止指令重排序优化。

 

注:本人把单例模式写成五种,其实单例模式概念上讲只有两种,一个是饿汉模式 和 懒汉模式 ,下面的三种方式都是在懒汉模式运行性能上的优化。如果只学习java基础,看前前两个就足够了。但是后面三种也不容小觑哦

以上是关于java中的单例模式的主要内容,如果未能解决你的问题,请参考以下文章

Java中的单例设计模式举例

JAVA中的单例设计模式-分为懒汉式和饿汉式(附代码演示)

JAVA中的单例设计模式-分为懒汉式和饿汉式(附代码演示)

JAVA中的单例设计模式-分为懒汉式和饿汉式(附代码演示)

片段作为 Android 中的单例

Java中的单例模式