单例模式定义

在软件系统中,一个类只有一个实例对象。(该类只提供一个取得实例的静态方法)

推荐使用的三种单例模式

  • DoubleCheck
  • 静态内部类
  • 枚举

1.DoubleCheck 双重检查

特点:效率高,线程安全,延迟加载。

class DoubleCheck
{
    private static volatile DoubleCheck instance;

    private DoubleCheck(){}

    public static DoubleCheck getInstance()
    {
        /*
        DoubleCheck如何实现?线程安全和效率提升
        
        在多线程的环境下,假设线程A直接进入#2,实例化对象。
        且实例化方法外用synchronized修饰,所以是线程安全的。
        当线程A实例化对象结束,对象instance已经被创建,执行到#1的线程将会直接调到#3,返回instance
        
        且DoubleCheck实现了延迟加载(new在方法里)
         */
        if(instance==null)                               //#1
        {
            synchronized (DoubleCheck.class)             //#2
            {
                if(instance==null)
                {
                    instance = new DoubleCheck();        //#3
                }
            }
        }
        return instance;
    }
}


public class Operation
{
    public static void main(String[] args) {
        DoubleCheck doubleCheck1 = DoubleCheck.getInstance();
        DoubleCheck doubleCheck2 = DoubleCheck.getInstance();

        System.out.println(doubleCheck1.hashCode());
        System.out.println(doubleCheck2.hashCode());


    }
}

2.静态内部类

特点:通过JVM类加载避免了线程安全问题,延迟加载,效率高。

class StaticClassInner {

    private StaticClassInner() {}



    /*
     使用静态内部类,实现了延迟加载
     调用getInstance()方法时,才会加载StaticClassInnerInstance。

     通过JVM类加载线程安全的机制,避免了线程不安全。

     */
    private static class StaticClassInnerInstance {
        private static final StaticClassInner INSTANCE = new StaticClassInner();
    }

    public static StaticClassInner getInstance() {
        return StaticClassInnerInstance.INSTANCE;
    }
}

public class Operation
{
    public static void main(String[] args) {
        StaticClassInner doubleCheck1 = StaticClassInner.getInstance();
        StaticClassInner doubleCheck2 = StaticClassInner.getInstance();

        System.out.println(doubleCheck1.hashCode());
        System.out.println(doubleCheck2.hashCode());


    }
}

3.枚举

Effective Java作者Josh Bloch推荐。

enum Hq
{
    INSTANCE;

    public void printf()
    {
        System.out.println("ins");
    }
}

public class Operation
{
    public static void main(String[] args) {
        Hq hq = Hq.INSTANCE;
        Hq hqq = Hq.INSTANCE;

        System.out.println(hq.hashCode());
        System.out.println(hqq.hashCode());
    }
}

饿汉式的延迟加载问题(可用)

如果创建的对象一定会被使用,那么可以忽略内存浪费的问题。

class SingleTom
{
    private SingleTom()
    { }

    /*
    在静态常量中实例化对象,无法实现延迟加载,如果对象未被使用,会造成内存浪费。(#1)
    无线程安全问题

    将实例化对象放于静态代码块中并无实际作用
     */
    private final static SingleTom instance;  // = new SingleTom();#1

    static  //#2
    {
        instance=new SingleTom();
    }


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

public class Operation
{
    public static void main(String[] args) {
        SingleTom hq = SingleTom.getInstance();
        SingleTom hqq = SingleTom.getInstance();

        System.out.println(hq.hashCode());
        System.out.println(hqq.hashCode());
    }
}

懒汉式的线程安全问题(不可用)

class Singletom {

    private static Singletom singleton;

    private Singletom() {}

    /*
    线程不安全:

    调用getInsyance()方法时,如果同时有多个线程同时进入到#1
    就会创建多个实例对象

    倘若在方法上加上syn关键字,线程同步问题解决,但效率大大降低
    
    doublecheck大概就是在这种纠结下选择用两次if(singleton == null)来控制线程同步和效率问题

     */
    public static /*synchronized*/ Singletom getInstance() {
        if (singleton == null) {                   //#1
            singleton = new Singletom();
        }
        return singleton;
    }
}

public class Operation
{
    public static void main(String[] args) {
        Singletom hq = Singletom.getInstance();
        Singletom hqq = Singletom.getInstance();

        System.out.println(hq.hashCode());
        System.out.println(hqq.hashCode());
    }
}

参考文档:

单例模式的八种写法比较

版权声明:本文为noneplus原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/noneplus/p/11336752.html