单例模式是设计模式的一种,先谈谈什么是设计模式?
大家应该都知道棋谱、剑谱之类的,就是一些“高手”在经历过长期的累计之后,更具经验写出的具有固定套路的处理“方法”,只要按照这个套路来,在对局之中必然是不会吃亏的,甚至能够一招制敌。
那么在我们日常的开发中也有大佬们针对一些十分常见的场景,抽象出固定的套路。一些小白在学习了大佬总结的设计模式之后,就像是掌握了套路,针对适用的场景就不会吃亏。
单例模式的使用场景是什么呢?单例模式能够保证某个类的在程序中只存在唯一的一份,就算是new多个实例,实际上也是同一份。
这一点在很多场景上都需要. 比如 JDBC 中的 DataSource 实例就只需要一个.
单例模式的具体实现方式可以分为 饿汉模式 和 懒汉模式:
饿汉模式:
类加载的同时创建实例。
- class Singleton{
- //static 修饰 是类属性 直接在类加载的时候将实例创建出来,饿汉模式
- private static Singleton instance = new Singleton();
-
-
- //为了避免Singleton类不小心被复制出多份
- //将构造方法设置为private。这样在类外面就没有办法通过new的方式来创建 Singleton这个实例了
- private Singleton() {
-
- }
-
- public static Singleton getInstance(){
- return instance;
- }
- }
懒汉模式-单线程版本
类加载的时候不创建实例. 第一次使用的时候才创建实例.
- class SingletonLazy{
- private static SingletonLazy instance = null;
-
- private SingletonLazy(){
-
- }
-
- public static SingletonLazy getInstance(){
- if(instance == null){
- instance = new SingletonLazy();
- }
- return instance;
- }
- }
懒汉模式-多线程版本
为什么这里的懒汉模式需要考虑多线程版本呢?因为上面的单线程的代码存在线程安全的问题。不安全是因为在getInstance当中有对instance的写操作,涉及到写操作的时候就要小心了。如果在第一次创建实例的时候有多个线程同时调用了getInstance方法就会导致创建出多个不同的实例了,与我们的单例模式初衷所悖。因此需要对这里的写操作加锁:
- class Singleton {
- private static Singleton instance = null;
- private Singleton() {}
- public synchronized static Singleton getInstance() {
- if (instance == null) {
- instance = new Singleton();
- }
- return instance;
- }
- }
懒汉模式-多线程改进版本
上面的代码还存在着两个不足:
- class SingletonThread {
- private static volatile SingletonThread instance = null;
-
- private SingletonThread() {
-
- }
-
- public static SingletonThread getInstance() {
- if (instance == null) {
- synchronized (SingletonThread.class) {
- if (instance == null) {
- instance = new SingletonThread();
- }
- }
- }
-
- return instance;
- }
- }
注意理解两个if判定。
虽然两个if的判定条件是一样的但是两者的含义却大大不同。第一个if判定是判定instance是否是第一次创建,如果没有创建的话就开始创建实例并且加锁。第二个if判定抢夺到锁后,instance是否已经被创建了。两个if语句不可以省略任意一个。