单例模式

        定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

        Singleton:负责创建Singleton类自己的唯一实例,并提供一个getInstance的方法,让外部来访问这个类的唯一实例。


        懒汉式:

/**
* 懒汉式单例实现示例
*/
public class Singleton {
        
        private static Singleton uniqueInstance = null;

        private Singleton() {}

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

        饿汉式:

/**
* 饿汉式单例实现的实现
*/
public class Singleton {

        private Singleton uniqueInstance = new Singleton();

        private Singleton() {}

        public static Singleton getInstance() {
                return uniqueInstance;
        }

}

        饿汉式、懒汉式其实是一种比较形象的称谓,所谓饿汉式,既然饿,那么在创建对象实例的时候就比较着急,于是就在装载类的时候就创建对象实例。

        所谓懒汉式,既然是懒,那么在创建对象实例的时候就不着急,会一直等到马上要使用对象实例的时候才会创建,懒人嘛,总是推托不开的时候才去真正执行工作,因此在装载对象的时候不创建对象实例。


        懒汉式单例应用了延迟加载的思想——一开始不要加载资源或者数据,一直等,等到马上就要使用这个资源或者数据了,躲不过去了才加载。


        不加同步的懒汉式是线程不安全的,而饿汉式是线程安全的,因为虚拟机保证只会装载一次,在装载类的时候不会发生并发的。


        加了同步的懒汉式模式,即用synchronized修饰静态方法,是线程安全的,但是这样一来,会降低整个访问的速度,而且每次都要判断。


        可以使用“双重检查加锁”的方式来实现,就可以既实现线程安全,又能够使性能不受到很大的影响。

        所谓双重检查加锁机制,指的是:并不是每次进入getInstance方法都需要同步,而是先不同步,进入方法过后,先检查实例是否存在,如果不存在才进入下面的同步块,这是第一重检查。进入同步块后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查。这样一来,就只需要同步一次了,从而减少了多次在同步情况下进行判断所浪费的时间。

        双重检查加锁机制的实现会使用一个关键字volatile,它的意思是:被volatile修饰的变量的值将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确地处理该变量。

public class Singleton {

        private volatile static Singleton instance = null;

        private Singleton() {}

        public static Singleton getInstance() {

                if (instantce == null) {  // 第一次检查
                        synchronized(Singleton.class) {
                                if (instance == null) {  // 第二次检查
                                        instance = new Singleton();
                                }
                        }
                }
                return instance;
        }
}

        在某些情况下,JVM已经隐含地执行了同步,这些情况下就不用自己再来进行同步控制了,这些情况包括:

        (1) 由静态初始化器(在静态字段上或static{}块中的初始化器)初始化数据时;

        (2) 访问final字段时;

        (3) 在创建线程之前创建对象时;

        (4) 线程可以看见它将要处理的对象时。

        既然这样,要想很简单地实现线程安全,可以采用静态初始化器的方式,它可以由JVM来保证线程的安全性。

public class Singleton {

        /**
        * 类级的内部类,也就是静态成员的成员式内部类,该内部类的实例与外部类的实现
        * 没有绑定关系,而且只有被调用时才会装载,从而实现了延迟加载
        */
        public static class SingletonHolder {
                /**
                 * 静态初始化器,由JVM来保证线程安全
                 */
                private static Singleton instance = new Singleton();
        }

        private Singleton() {}
    
        public static Singleton getInstance() {
                return SingletonHandler.instance;
        }
}

        当getInstance方法第一次被调用的时候,它第一次读取SingletonHandler.instance,导致SingletonHandler类得到初始化:而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton的实例,由于是静态的域,因此只会在虚拟机装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。

        这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。


        然而,单元素的枚举类型已经成为实现Singleton的最佳方法。

        Java的枚举类型实质上是功能齐全的类,因此可以有自己的属性和方法。Java枚举类型的基本思想是通过公有的静态final域为每个枚举常量导出实现的类。从某个角度讲,枚举是单例的泛型化,本质上是单元素的枚举。

/**
* 使用枚举来实现单例模式的示例
*/
public enum Singleton {

        /**
        * 定义一个枚举的元素,它就代表了Singleton的一个实例
        */
        uniqueInstance;

        public void singletonOperation() {
                // 示例方法
        }
}



        

《研磨设计模式》
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值