单例模式
1,基本思想
确保类在内存中只有一个对象,该类必须自行创建,并且对外提供访问。
2,如何实现
a,私有构造方法;
b,自己创建一个私有对象;
c,提供一个公共方法供外界访问。
本文介绍两种单例设计模式:饿汉式和懒汉式。
3,饿汉式
package com.test.single;
/**
* 饿汉式
* @author Administrator
*
*/
public class Student {
//1,构造私有
private Student() {
}
//2,自行创建对象
private static Student s = new Student();
//3,对外提供访问
public static Student getInstance() {
return s;
}
}
4,懒汉式
package com.test.single;
/**
* 懒汉式
* @author Administrator
*
*/
public class Teacher {
//1,私有化构造方法
private Teacher() {
}
//2,私有化的对象实例置为null,保证在用的时候才去创建对象
private static Teacher t = null;
//3,对外提供访问,注意线程安全问题
public synchronized static Teacher getInstance() {
if(t == null) {
//此处可能有线程安全问题
t = new Teacher();
}
return t;
}
}
5,两者对比
a,饿汉式是在类加载的时候就创建对象(饿汉是饿了就吃饭);
b,懒汉式是在用的时候才去创建对象(懒汉是不到饿死就不吃饭)。
注意:开发中多用饿汉式,因为饿汉式是不会出问题的单例模式。
懒汉式的两种思想:
a,懒加载或延迟加载思想;
b,懒汉式的线程安全问题:
线程安全问题出现的前提:处在多线程环境中,有共享数据,而且有多条语句操作共享数据。
在懒汉式的情况下:懒汉式例子中的”t”对象就是共享数据,”t == null”和”t = new Teacher();”就是多条语句在操作”t”对象,如果处在多线程环境下,有三个线程thread1,thread2和thread3都进到了” if(t == null){ thread1,thread2,thread3}”中,这时候每个线程都会new一个”t”对象,这就是懒汉式的线程安全问题。
6,双重检查加锁机制
”synchronized”解决了懒汉式的线程安全问题,但是这样一来,每次都要在同步的情况下进行判断,浪费时间,也降低了整个访问的速度,所以针对懒汉式的线程安全问题,提出了“双重检查加锁机制”。
package com.test.single;
/**
* 双重检查加锁机制
* @author Administrator
*
*/
public class Singleton {
//1,私有化构造方法
private Singleton() {
}
//2,volatile修饰的私有化对象
private volatile static Singleton singleton = null;
//3,对外提供访问
public static Singleton getInstance() {
//先检查实例是否存在,不存在进入同步块,存在就直接返回实例
if(singleton == null) {
//同步块,线程安全的创建实例
synchronized (Singleton.class) {
//再次检查实例是否存在,不存在才真正的创建实例
if(singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
双重检查加锁机制,并不是每次进入”getInstance”方法都需要同步,而是先不同步,进入方法之后先检查实例是否存在,如果不存在才进入下面的同步代码块,这也是第一重检查,如果存在就直接返回实例了。进入同步代码块之后,再次检查实例实例是否存在,如果不存在就在同步的情况下进行实例,这是第二重检查,这样一来就只需要同步一次了,就减少了多次在同步的情况下进行判断所浪费的时间。
双重检查加锁机制的实现会用到一个关键字”volatile”,它的意思是:被”volatile”修饰的变量的值将不会被本地线程缓存,所有对该变量的读写操作都是直接操作内存,从而确保多个线程能正确的处理该变量。
这种实现方式即可以实现线程安全,又不会对性能造成太大的影响,它只是在第一次创建实例的时候进行同步,以后就不再需要同步,加快了运行速度。
注意:
a,在java1.4及以前的版本中,很多jvm对于”volatile”的实现有问题,会导致双重检查加锁机制失败,因此该机制只能用在java5及以上的版本;
b,由于”volatile”可能会屏蔽掉虚拟机中一些必要的代码优化,所以运行效率并不是很高,因此建议一般没有特别的需要就不要使用。
7,单例优点
优点:在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象,单例模式可以大大提高系统的性能;
缺点:不易扩展。