设计模式(3)---单例模式
单例模式 Singleton (创建型模式)
1.定义
单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点。
2.结构图
3.代码
懒汉式
1 /*
2 * 懒汉式
3 * 实现了延迟创建和保证了线程安全
4 */
5 public class Singleton {
6
7 private static Singleton instance = null ;
8
9 private Singleton() {
10
11 }
12
13 public static synchronized Singleton getInstance(){
14 if (instance == null){
15 instance = new Singleton() ;
16 }
17 return instance ;
18 }
19
20 }
双重检查加锁
synchronized对整个方法加锁是没必要的,只要保证实例化对象的那段代码不被多线程同时访问就行了,当两个线程同时访问这个方法时,假设这时对象还没有被实例化,他们都可以通过第一重instance==null的判断,然后由于lock机制,这两个线程只有一个能进入,如果没有第二重的判断,则第一个线程创建了实例后,第二个线程还是可以继续再创建新的实例。
1 /*
2 * 双重检查加锁
3 *
4 */
5 public class Singleton {
6
7 private volatile static Singleton instance = null ;
8
9 private Singleton() {
10
11 }
12
13 public static Singleton getInstance(){
14 if (instance == null){
15 synchronized(Singleton.class){
16 if (instance == null){
17 instance = new Singleton() ;
18 }
19 }
20 }
21 return instance ;
22 }
23
24 }
Instance 采用 volatile 关键字修饰也是很有必要的。
Instance = new Singleton(); 这段代码其实是分为三步执行。
- 分配内存空间。
- 初始化对象。
- 将 Instance 指向分配的内存地址。
但是由于 JVM 具有指令重排的特性,有可能执行顺序变为了 1>3>2,这在单线程情况下自然是没有问题。但如果是多线程就有可能 B 线程获得是一个还没有被初始化的对象以致于程序出错。
所以使用 volatile 修饰的目的是禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。
采用静态内部类的形式
Java中静态内部类可以访问其外部类的成员属性和方法,同时,静态内部类只有当被调用的时候才开始首次被加载,利用此特性,可以实现懒汉式,在静态内部类中静态初始化外部类的单一实例即可。
既是线程安全的,同时又提升了性能
1 /*
2 * 懒汉形式改进版 采用了静态内部类
3 *
4 */
5 public class Singleton {
6
7 private Singleton() {
8
9 }
10 private static class LazyHolder{
11 private static final Singleton instance = new Singleton();
12 }
13
14 public static Singleton getInstance(){
15 return LazyHolder.instance ;
16 }
17
18 }
或者
1 /**
2 * 请求内存队列
3 * @author Administrator
4 *
5 */
6 public class RequestQueue {
7
8 /**
9 * 单例有很多种方式去实现:我采取绝对线程安全的一种方式
10 *
11 * 静态内部类的方式,去初始化单例
12 *
13 * @author Administrator
14 *
15 */
16 private static class Singleton {
17
18 private static RequestQueue instance;
19
20 static {
21 instance = new RequestQueue();
22 }
23
24 public static RequestQueue getInstance() {
25 return instance;
26 }
27
28 }
29
30 /**
31 * jvm的机制去保证多线程并发安全
32 *
33 * 内部类的初始化,一定只会发生一次,不管多少个线程并发去初始化
34 *
35 * @return
36 */
37 public static RequestQueue getInstance() {
38 return Singleton.getInstance();
39 }
40
41
42
43 }
饿汉形式
1 /*
2 * 饿汉形式
3 *
4 */
5 public class Singleton {
6
7 private static final Singleton instance = new Singleton() ;
8
9 private Singleton() {
10
11 }
12
13 public static Singleton getInstance(){
14 return instance ;
15 }
16
17 }
饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,
而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。
饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成,
而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。