浅谈23种设计模式(java版)
单例模式
-
什么是设计模式
- 设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结,使用设计模式的目的是为了提高代码的可重用性,使得代码更加符合人类的理解思维。
-
什么是单例模式
- 单例顾名思义在整个系统中只有一个类的实例对象。
-
这有什么好处
- 有一些对象我们确实只需要一个,例如,线程池,缓存,对话框,日志对象等等,就只能有一个实例,如果创建多个实例,就会导致许多问题,例如程序运行异常,运行结果不一致等问题。
-
不简单的单例模式
- 虽然单例模式听起来简单易懂,但是真正想实现一个单例模式也不是那么容易的,如何保证一个对象只能实例化一次,如何保证多线程下的线程安全问题等等。
-
接下来让我们一步一步的来实现单例模式
-
如何创建一个对象?
new Singleton();
-
如果其它对象想创建Singleton会怎么样? 可以再次创建吗?
是的,当然可以
-
如果有一个类我们是否可以多次实例化它?
答案是:当然可以了, 只要你的类是公开的
-
如果不公开类会怎么样?
如果不公开类,那只有同一个包内的类可以实例化它,并可以实例化多次(小伙子你好抠)
-
我们可以让外部无法实例化它吗?
当然可以了,如果我们把它的构造方器定义为私有的,除了它的内部类可以初始化它,其它对象都不能初始化它,当然也就不能实例化它(让人活不了)
public Singleton{ private Singleton(){ } }
-
我觉得私有构造器是不是有点不合理?
没错确实有点不合理,因为我们必须有
Singleton
类的实例才可以调用该类的构造器,但没有类能实例化它,所以我们得不到它的实例,这就好比 “鸡生蛋,蛋生鸡”,我们只能在类内部调用构造器。 -
我有个想法你认为如何?
如果我们在类的内部创建一个静态方法去调用它如何。
public Singleton{ private Singleton(){ } public static Singleton getInstance(){ } }
-
为什么 调用
getInstance()
不用对象名而用类名?因为
getInstance()
方法是静态的,静态方法是属于类而非属于对象的,所以调用它只能用类名。 -
有意思,如果把上面的组合在一起会如何?
我们就能够在
getInstance()
方法中愉快的进行实例的创建了。public Singleton{ private Singleton(){ } public static Singleton getInstance(){ return new Singleton(); } }
-
我们是否解决了实例化问题?
对,我们已经可以实例化对象了,但也仅仅是实例化对象,并没有保证对象唯一。
-
如何保证对象的唯一性?
我们改造一下上面的代码
//懒汉模式 public Singleton{ //保存实例化的对象 private static Singleton singleton = null; private Singleton(){ } public static Singleton getInstance(){ if(singleton == null){ return new Singleton(); } else{ return singleton; } } }
//饿汉模式 public Singleton{ //类加载时候就创建对象(因为是静态变量所以只加载一次保证了实例唯一) private static Singleton singleton = new Singleton()l; private Singleton(){ } public static Singleton getInstance(){ return singleton; } }
以上两种模式创建单例对象。
-
有个问题出现了如果在多线程情况下会如何?
我们发现在懒汉模式下可能会出现多个线程创建多个实例的情况,这就违背了我们单例原则了。
-
如何解决多线程下的安全问题?
使用
synchronized
关键字同步代码块//懒汉模式 public Singleton{ //保存实例化的对象 private static Singleton singleton = null; private Singleton(){ } //加入同步关键字使得同一时间只能有一个线程实例化该类 public static synchronized Singleton getInstance(){ if(singleton == null){ return new Singleton(); } else{ return singleton; } } }
但是我们只有第一次执行才会同步,显然上面的每次执行都同步影响效率
-
我们如何改善多线程?
使用双重锁 首先检查实例是否创建,如果未创建才会同步,并且只有第一次同步。
//懒汉模式 public Singleton{ //使用volatile关键字 保证当Single 创建时,多线程能正确处理 private volatile static Singleton singleton = null; private Singleton(){ } //加入同步关键字使得同一时间只能有一个线程实例化该类 public static Singleton getInstance(){ if(singleton == null){ synchronized (Singleton.class){ if(singleton == null){ singleton = new Singleton(); } } } return singleton; } }
-
总结
看到这里是不是在想单例也不是那么容易创建的,需要考虑如何保证唯一实例,多线程下如何保证实例唯一,如何优化多线程等等,但是只要我们一步一步的学习,不要操之过急,认真思考,多加练习,再难的设计模式也能学会。 后续我会加入工厂模式继续优化单例的创建
-