单例模式几种使用方法

单例模式特点:

单例类只能有一个实例。
单例类必须自己创建自己的唯一实例。
单例类必须给所有其他对象提供这一实例。

1.饿汉式单例

public class HungrySingleton {
    public static HungrySingleton instance = new HungrySingleton();
    /**
    *默认创建一个私有构造类
    */
    private void HungrySingleton(){}
    
    /**
    *静态工厂方法
    */
    public static HungrySingleton getInstance() {
        return instance;
    }
}

饿汉式是典型的空间换时间,在类初始化加载的时候就创建了对象,在调用的时候节省了创建的时间。

2.懒汉式单例

public class LazySingleton {
    public static LazySingleton instance = null;
    /**
    *默认创建一个私有构造类
    */
    private void LazySingleton(){}
    
     /**
    *静态工厂方法
    */
    public static synchronized LazySingleton getInstance() {
        if (instance == null) {
            instance = = new LazySingleton();
        }  
        return instance;
    }
}

1.懒汉式特点,在类加载是不创建对象,调用getInstance()方法时如果instance为null才去创建对象。 2.而且在方法名前加了synchronized锁,是线程安全的,但是有一个弊端每次都要判断是否为空,比较慢(时间换空间)

3.双重检查加锁

public class DoubleCheckSingleton {
    public volatile static DoubleCheckSingleton instance = null;
    /**
    *默认创建一个私有构造类
    */
    private void DoubleCheckSingleton(){}
    
     /**
    *静态工厂方法
    */
    public static LazySingleton getInstance() {
        if (instance == null) {
            //同步代码块
            synchronized (DoubleCheckSingleton.class) {
                if (instance == null) {
                    instance = = new LazySingleton();
                }
            }
        }  
        return instance;
    }
}

双重检查主要是先不在方法中加锁(防止速度变慢),先判断instance是否为空,然后再使用同步代码块,再次判断instance是否为空,为空的话创建对象。

优势在于不用每次都同步,只有在instance为null的时候才创建对象,(优于懒汉式

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

:由于volatile关键字可能会屏蔽掉虚拟机中一些必要的代码优化,所以运行效率并不是很高。因此一般建议,没有特别的需要,不要使用。也就是说,虽然可以使用“双重检查加锁”机制来实现线程安全的单例,但并不建议大量采用,可以根据情况来选用(volatile又会影响速度,所有懒汉式和双重加锁都有缺陷,建议尽量减少使用)

4.Lazy initialization holder class模式

在多线程开发中,为了解决并发问题,主要是通过使用synchronized来加互斥锁进行同步控制。但是在某些情况中,JVM已经隐含地为您执行了同步,这些情况下就不用自己再来进行同步控制了。这些情况包括:

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

2.访问final字段时

3.在创建线程之前创建对象时

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

:类加载时会先加载static变量和static代码块(static{})

解决方案的思路

要想很简单地实现线程安全,可以采用静态初始化器的方式,它可以由JVM来保证线程的安全性。比如前面的饿汉式实现方式。但是这样一来,不是会浪费一定的空间吗?因为这种实现方式,会在类装载的时候就初始化对象,不管你需不需要。

如果现在有一种方法能够让类装载的时候不去初始化对象,那不就解决问题了?一种可行的方式就是采用类级内部类,在这个类级内部类里面去创建对象实例。这样一来,只要不使用到这个类级内部类,那就不会创建对象实例,从而同时实现延迟加载和线程安全。

public class Singleton {

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

当getInstance方法第一次被调用的时候,它第一次读取SingletonHolder.instance,导致SingletonHolder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton的实例,由于是静态的域,因此只会在虚拟机装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。

参考资料:http://www.cnblogs.com/java-my-life/archive/2012/03/31/2425631.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值