java单例模式.md

java单例模式

单例模式(Singleton Pattern)算是一个最简单的设计模式了,单例模式属于创建型模式,提供了一种创建对象的最佳方式。单例,顾名思义就是确保只有一个实例被创建。下面来说说我理解的一些思路。

通常我们创建实例对象都是通过new来创建,但是每次调用new都会创建不同的对象。这在单例模式肯定是不允许的,所以第一步,我们要将无参的构造函数私有化,对外断了new的这条路,但是内部其实还是用new()来创建对象的,只是这个对象是私有的。那这样外面要如何来获得这个对象呢?类里面经常有private int age这类私有的成员变量,我们通常都是用get和set方法来访问的,同样,单例模式也是通过方法来获得对象。好了,说了这么多,还是来看看具体的代码。

简单实现(饿汉式)

public class Singleton {
 	//创建私有化实例对象
   private static final Singleton instance = new Singleton();
   //构造函数私有化
   private Singleton(){}
 	//通过方法来获得实例对象
   public static Singleton getInstance(){
      return instance;
   }
}

这么一看单例模式代码还真少,其实也就三行(注释的部分),当然还有一点前面忘了说,就是方法要声明成static,不然外面根本没办法调用这个方法,因为无法用new来创建对象。既然方法是静态方法,那成员变量也要加上static,声明成类变量。一个简单的单例模式就是这样了,真的是太简单了,当初我临时抱佛脚,看了几遍代码就觉得掌握了,结果过了一段时间这么简单的都记不得了,现在好好理解一下想忘掉估计都很难。

其实这个就是饿汉式单例模式,并且是线程安全的,这种线程安全没有用到锁的机制,执行效率高,推荐使用(所以这个就是一个比较好的答案了,赶时间的同学下面可以不用看了)。但是有个缺点,实例instance并不是第一次调用属性Singleton.getInstance的时候才调用,而是第一次用到Singleton这个类的时候就会初始化instance。比如这个类里面有其他的静态方法,调用这个静态方法的时候instance也会被初始化,这样就浪费了内存,我现在还不需要你,你创建出来干嘛?这点可以向下面的懒加载学习。

懒汉式

来说说懒汉式,你可能听过lazy-load懒加载,就是当这东西要用到的时候再去加载,懒汉式差不多就是这样(这个例子不是很恰当,但是可以这么理解)。和前面的代码相比就是修改一下而已。

public class Singleton {
 	//这里没有 = new Singleton(),也就是没有创建对象
   private static Singleton instance;
   private Singleton(){}
   public static Singleton getInstance(){
    //对象如果已经创建过了就直接返回这个对象
     if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
   }
}

这个就是懒汉式的代码,可以和前面的饿汉式对照一下。懒汉式不是线程安全的,这是懒汉式的致命缺陷,想要有懒汉式和饿汉式的优点?看看下面这个。

双检锁/双重校验锁

(DCL,即 double-checked locking),就是在前面懒汉基础上再加上一个null检查。

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

首先看到有两个if判断,有人可能会想只要一个就好了,为什么需要两个呢?第一个if很好理解,如果已经创建了实例了,就不再创建了,而且第一个if可以避免进入到加锁的操作。为什么需要第二个if呢?试想一下,在多线程的情况下,有两个线程同时通过了第一个if的判断,这时一个线程会执行加锁的部分,而另一个线程则等待锁释放。如果没有第二个if,第一个线程创建实例后释放锁,第二个线程则会继续创建实例,这样就出现了两个实例了,所以这里需要有两个if来确保不会发生问题。
注意和前面不同的是我们用到了volatile关键字,volatile能够提供可见性,并且禁止指令重排序。因为new Singleton()创建对象不具有原子性,实例化对象大体分成三步

  1. 分配对象的内存空间
  2. 初始化对象
  3. 将变量指向分配的内存空间

由于存在指令重排列,可能先执行1,3,再执行2,当执行1,3之后,instance就不等于null了,所以其他线程在第一个if判断的时候就会将instance返回,但是这个实例还没有初始化,是不完整的实例,于是出现了错误。所以我们需要使用volatile关键字。
这种方法用到了锁的机制,锁比较耗时,所以相比较之下还是推荐使用饿汉式或者接下来提到的其他方法。

静态内部类

按需创建实例,线程安全。弥补前面饿汉式的缺点,当Singleton被装载了,instance也不一定会实例化。因为SingletonHolder这个静态内部类没有主动使用。并且没有用到锁,效率高。

public class Singleton {
        private static class SingletonHolder{
            private static final Singleton INSTANCE = new Singleton();
        }

        private Singleton() {}

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

枚举

用枚举的方式实现。实现单例模式的最佳方法。它更简洁,线程安全,自动支持序列化机制,止反序列化重新创建新的对象,绝对防止多次实例化。

    public enum Singleton6 {
        INSTANCE;
        public void whateverMethod() {

        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值