VirgilG72's Blog.

单例模式

字数统计: 1.3k阅读时长: 5 min
2019/03/12 Share

想法来源:在学习使用retrofit网络请求框架时,学习了retrofit框架的源码分析,源码中用到了不少设计模式的思想 如:建造者模式、装饰模式、外观模式、代理模式、策略模式、工厂模式、单例模式等等,再加上之前的CVTE面试考察了设计模式的知识点,于是乎决定做个Java设计模式的总结。第一篇献给单例模式,因为单例模式最为常用,而且用法最多。

1.单例模式介绍

1.1单例模式说明

实现一个类只有1个实例化对象&提供一个全局访问点


1.2作用

保证一个类只有一个对象,降低耦合度


1.3单例模式优点

  • 提供了对唯一实例的受控访问
  • 由于一个类只有一个对象,节约系统资源,提高系统性能,降低耦合度

1.4单例模式缺点

  1. 单例类的职责过重,违背了“单一职责原则”
  2. 如果实例化对象长时间不使用,会被系统当成垃圾回收

2.单例模式的实现方式

2.1 饿汉式

1
2
3
4
5
6
7
8
9
10
11
12
public class Singleton {
private static Singleton ourinstance =new Singleton();//JVM加载该类时,实例化对象,保证只有一个类只有一个对象

//使用private关键字修饰构造方法,封锁Singleton,防止被实例化对象
private Singleton(){

}
//获取唯一对象的方法
public static Singleton getInstance(){
return ourinstance;
}
}

饿汉式优点:单例对象 线程安全 占用内存小,运行速度快

2.2 enum枚举类型

1
2
3
4
5
6
7
package Singleton;

public enum Enum {
Instance;
}
//获取单例的方式:
//Enum instance=Enum.Instance;

枚举型优点:最简单、易用的单例模式,是实现单例模式的最佳方法

2.3 懒汉式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package Singleton;

public class Lanhan {
//加载类时,先不实例化对象
private static Lanhan ourinstance =null;

//使用private关键字修饰构造方法,封锁Singleton,防止被实例化对象
private Lanhan(){

}
//手动实例化对象,延迟加载
public static Lanhan newinstance() {
if (ourinstance==null) {
ourinstance=new Lanhan();

}
return ourinstance;
}
}

懒汉型优点:创建单例时机可控,延迟加载,手动实例化对象
懒汉型缺点:多线程下不安全,假设线程A、B在并发创建对象,线程A执行到ourinstance=new Lanhan();时(实例化对象时需要时间),线程B执行到if (ourinstance==null)的判断条件中,此时判断为true,于是乎,线程A、B各实例化出对象,共有两个对象,违背了单例模式的原则

2.4同步锁(懒汉式的改进)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//写法1
package Singleton;

public class Lanhan {
//加载类时,先不实例化对象
private static Lanhan ourinstance =null;

//使用private关键字修饰构造方法,封锁Singleton,防止被实例化对象
private Lanhan(){

}
//加入同步锁,线程A执行该方法时,其他线程进入阻塞状态,直到线程A执行完该方法,才让其他线程执行方法。保证线程安全
public synchronized static Lanhan newinstance() {
if (ourinstance==null) {
ourinstance=new Lanhan();

}
return ourinstance;
}
}

//写法2
package Singleton;

public class Lanhan {
//加载类时,先不实例化对象
private static Lanhan ourinstance =null;

//使用private关键字修饰构造方法,封锁Singleton,防止被实例化对象
private Lanhan(){

}
//加入同步锁,线程A执行该方法时,其他线程进入阻塞状态,直到线程A执行完该方法,才让其他线程执行方法。保证线程安全
public static Lanhan newinstance() {
synchronized (Lanhan.class) {
if (ourinstance==null) {
ourinstance=new Lanhan();

}
}
return ourinstance;
}
}

同步锁优点:加入同步锁后,保证线程安全
同步锁缺点:每次执行该方法,都要线程同步,造成过多的成本开销(加锁的时候耗时、耗能)

2.5双重校验锁(同步锁的改进)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package Singleton;

public class doubleif {
//加载类时,先不实例化对象
public static doubleif ourinstance =null;

//使用private关键字修饰构造方法,封锁Singleton,防止被实例化对象
private doubleif() {

}
//实现双重校验锁,添加一个判断条件,只有在ourinstance为空时,才加入同步锁,在保证线性安全的同时,提升了系统性能。否则,直接返回单例对象。
private static doubleif getInstance() {
if (ourinstance==null) {
synchronized (doubleif.class) {
if (ourinstance==null) {

ourinstance =new doubleif();

}
}

}
return ourinstance;
}
}

双重校验锁优点:在保证线性安全的同时,提升了系统性能
双重校验锁缺点:多种判断,容易出错

2.6静态内部类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package Singleton;

public class inStatic {
//定义一个静态内部类,在静态内部类中实例化单例对象
private static class Instance{
private static inStatic ourinstance=new inStatic();
}
//使用private关键字修饰构造方法,封锁Singleton,防止被实例化对象
private inStatic() {

}
//执行此方法时,JVM加载静态内部类,而JVM只能加载一遍,在保证只生成一个实例化对象的同时,还能实现延迟加载功能。
public static inStatic getInstance (){
return Instance.ourinstance;
}
}

静态内部类优点:保证线程安全,节约资源,在保证只生成一个实例化对象的同时,还能实现延迟加载功能。

最后附上六大实现方式的总结与对比

CATALOG
  1. 1. 1.单例模式介绍
    1. 1.1. 1.1单例模式说明
    2. 1.2. 1.2作用
    3. 1.3. 1.3单例模式优点
    4. 1.4. 1.4单例模式缺点
  2. 2. 2.单例模式的实现方式
    1. 2.1. 2.1 饿汉式
    2. 2.2. 2.2 enum枚举类型
    3. 2.3. 2.3 懒汉式
    4. 2.4. 2.4同步锁(懒汉式的改进)
    5. 2.5. 2.5双重校验锁(同步锁的改进)
    6. 2.6. 2.6静态内部类
    7. 2.7. 最后附上六大实现方式的总结与对比