手记

Java设计模式----------单例模式

1、介绍

单例模式是一种创建型模式。该模式中,构造方法是私有的,类内自己创建实例化对象,并返回给外部调用。通过加锁能够保证该类只有一个实例化对象,节省系统资源和开销。

意图:保证一个类仅有一个实例,提供给外部调用。

主要解决:避免类被频繁地创建与销毁,增加性能消耗;避免内存中保存多份实例,增加内存开销。

何时使用:需要控制实例数目,节省系统资源的场景。

如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

关键代码:构造函数私有化,类内自己构造实例,

应用实例: 1、Windows的Task Manager(任务管理器) 2、多线程的线程池 3、网站的计数器

优点: 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例。 2、避免对资源的多重占用(比如写文件操作)。

缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

注意事项:getInstance() 方法需要使用synchronized关键字保证线程安全,避免多线程场景下实例化多个对象。

2、案例实现

在具体实现上,单例模式存在几种不同的实现方式。

方式一:Lazy loading + 非线程安全

实例化的动作在getInstance()函数中进行,属于Lazy loading。不使用synchronized关键字,所以是非线程安全的。

/**
 * Singleton.java
 */
public class Singleton
{
    private static Singleton singleton;

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

    private Singleton(){}

    public void sayHello()
    {
        System.out.println("Hello World!");
    }
}
/**
 * 验证
 */
public class Demo
{
    public static void main(String[] args)
    {
        Singleton instance = Singleton.getInstance();
        instance.sayHello();
    }

}

输出:
Hello World!

Process finished with exit code 0

方式二:Lazy loading + 线程安全

实例化的动作在getInstance()函数中进行,属于Lazy loading。使用synchronized关键字,所以是线程安全的。

/**
 * Singleton.java
 */
public class Singleton
{
    private static Singleton singleton;

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

    private Singleton(){}

    public void sayHello()
    {
        System.out.println("Hello World!");
    }
}
/**
 * 验证
 */
public class Demo
{
    public static void main(String[] args)
    {
        Singleton instance = Singleton.getInstance();
        instance.sayHello();
    }

}

输出:
Hello World!

Process finished with exit code 0

方式三:非Lazy loading + 线程安全

实例化的动作在singleton定义时进行,所以是在类加载时生成对象,直接占用内存空间。这种使用方式不需要加锁,所以执行效率比方式二要高。

/**
 * Singleton.java
 */
public class Singleton
{
    private static Singleton singleton = new Singleton();

    public static synchronized Singleton getInstance()
    {
        return  singleton;
    }

    private Singleton(){}

    public void sayHello()
    {
        System.out.println("Hello World!");
    }
}
/**
 * 验证
 */
public class Demo
{
    public static void main(String[] args)
    {
        Singleton instance = Singleton.getInstance();
        instance.sayHello();
    }

}

输出:
Hello World!

Process finished with exit code 0

方式四:双重检查锁定

getInstance()方法中,判断当singleton为空需要实例化的时候再进行加锁,减小了锁的粒度,相对于直接加锁性能上会更高。

new Singleton()操作是非原子的。所以用 volatile 关键字修饰singletonvolatile关键字影响内存可见性,保证线程对被修饰变量的所有更改都直接映射到JMM的主内存中,并且保证操作的有序性。在多线程环境下,如果不加volatile,那么指令的执行次序是存在乱序的。对于new Singleton()操作,最终翻译成字节码,是包含多条指令的。如果线程A和线程B都访问到单例,线程A获取到锁,在进行单例的初始化。由于指令的乱序执行,有可能是先分配了空间(这时候instance的值已经不是null了),再执行其他指令进行初始化。如果在分配完空间之后,其他指定执行之前,线程A被踢出了CPU,这时候线程B执行,检查内存中instance已经不是null,则获取并使用。但是此时instance并没有初始化完成,导致程序错误。

/**
 * Singleton.java
 */
public class Singleton
{
    private static volatile Singleton singleton;

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

    private Singleton(){}

    public void sayHello()
    {
        System.out.println("Hello World!");
    }
}
/**
 * 验证
 */
public class Demo
{
    public static void main(String[] args)
    {
        Singleton instance = Singleton.getInstance();
        instance.sayHello();
    }

}

输出:
Hello World!

Process finished with exit code 0

方式五:静态内部类

Singleton类被装载了,instance不一定被初始化。因为InnerSingleton类没有被主动使用,所以这种方式也具有Lazy loading的效果。

/**
 * Singleton.java
 */
public class Singleton
{
    private static class InnerSingleton
    {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance()
    {
        return InnerSingleton.INSTANCE;
    }

    private Singleton(){}

    public void sayHello()
    {
        System.out.println("Hello World!");
    }
}
/**
 * 验证
 */
public class Demo
{
    public static void main(String[] args)
    {
        Singleton instance = Singleton.getInstance();
        instance.sayHello();
    }

}

输出:
Hello World!

Process finished with exit code 0

1人推荐
随时随地看视频
慕课网APP