目录
1什么是单例模式
单例模式是指在内存中只会创建一次对象的设计模式,在程序中多次使用同一个对象且作用相同时,为了防止平凡的创建对象使得我们的内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所需的地方共同享用这一个对象.
对象调用分析图:
2.单例模式的种类
2.1懒汉式单例模式
懒汉式单例模式:在真正用到时才去创建对象,懒汉式创建对象的方法是在程序使用对象前,先判断该对象是否已经实例化(判空),若已实例化直接返回该类对象.否则则先执行实例化操作.
流程图:
代码:
/**
* <pre>
*懒汉式单例
* </pre>
*
*/
public class SingleTon {
private SingleTon (){}
private static SingleTon singleTon;
private static SingleTon getInstance(){
if (singleTon==null){
singleTon=new SingleTon();
}
return singleTon;
}
2.2饿汉式单例模式:
在类加载时就已将创建好对象,等待我们的程序去调用
饿汉式在类加载时已经创建好该对象,在程序调用时直接返回该单例对象即可,即我们在编写代码时就已经指明了要马上创建这个对象,不需要等到被调用时再去创建。
流程图:
代码:
/**
* <pre>
* 饿汉式单例
* </pre>
*
*/
public class SingleTon {
private SingleTon(){}
private static SingleTon singleTon=new SingleTon();
public static SingleTon getInstance(){
return singleTon;
}
注意:上面的代码已经实例化好了一个SingleTon对象在内存中,不会有多个SingleTon对象实例存在,类在加载时会在堆内存中创建一个SingleTon对象,当类被卸载时,SingleTon对象也随之消亡了.
3.懒汉式单例模式优化(线程问题和性能问题)
问题:经过上面的讲解懒汉式单例已经很好了但是在实际开发中有另一个场景,就是当两个线程同时判断singleTon时都会创建一个singleTon,所以我们需要解决一下线程问题
解决方法:最好的方式就是给我们的核心代码加锁;
//方法一
public static synchronized SingleTon getInstance() {
if (singleTon == null) {
singleTon = new SingleTon();
}
return singleTon;
}
// 方法二
public static SingleTon getInstance() {
synchronized{
if (singleTon == null) {
singleTon = new SingleTon();
}
}
return singleTon;
}
这样就规避了两个线程同时创建SingleTon对象的风险,但是引来另外一个问题:每次去获取对象都需要先获取锁,并发性能非常地差,极端情况下,可能会出现卡顿现象导致我们的性能变差
优化性能:如果没有实例化对象则加锁创建,如果已经实例化了,则不需要加锁,直接获取实例
代码:
//优化性能
public static SingleTon getInstance() {
if (singleTon == null) { // 线程A和线程B同时看到singleTon = null,如果不为null,则直接返回singleTon
synchronized{ // 线程A或线程B获得该锁进行初始化
if (singleTon == null) { // 其中一个线程进入该分支,另外一个线程则不会进入该分支
singleTon = new SingleTon();
}
}
}
return singleTon;
}
这样就完美解决了线程问题和性能问题了(进行了双重效验)
4.枚举实现
在JDK1.5随着枚举的出现单例模式又有了一个新的实现方式(利用枚举来实现)
代码:
/**
* <pre>
* 枚举单例
* </pre>
*
*/
public enum SingleTonEnum {
SINGLETON;
SingleTonEnum() {
System.out.println("枚举创建对象了");
}
public static void main(String[] args) {
test();
}
public static void test() {
SingleTonEnum t1 = SingleTonEnum.SINGLETON;
SingleTonEnum t2 = SingleTonEnum.SINGLETON;
System.out.print(t1 == t2);
}
}
控制台:
枚举创建单例模式的优势:
(1)代码对比饿汉式与懒汉式来说,更加地简洁其次,既然是实现单例模式,那这种写法必定满足单例模式的要求,而且使用枚举实现时,没有做任何额外的处理.
(2)它不需要做任何额外的操作去保证对象单一性与线程安全性.
5总结
(1)单例模式种类:懒汉式和饿汉式
(2)懒汉式:在需要用到对象时才实例化对象
(3)饿汉式:在类加载时已经创建好该单例对象,在获取单例对象时直接返回对象即可,不会存在并发安全和性能问题。
(4)在开发中如果对内存要求非常高,那么使用懒汉式写法,可以在特定时候才创建该对象;
(5)如果对内存要求不高使用饿汉式写法,因为简单不易出错,并且很好解决了并发安全和性能问题