单例模式(Singleton):保证一个类仅有一个实例,并提供一个访问它的全局访问点
通常来讲可以让一个全局变量使得一个对象被访问,但是它不能阻止你实例化多个对象,一个最好的方法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法
单例模式(Singleton)结构图
Singleton类:定义一个GetInstance操作,允许客户访问它的唯一实例。GetInstance是一个静态方法,主要负责创建自己的唯一实例
单例模式的好处:除了保证唯一实例外,还包括单例模式因为Singleton类封装了它的唯一实例,这样它就可以严格的控制客户怎样访问它以及何时访问 它,就是对唯一实例的受控访问
懒汉模式
package singleton;
public class Singleton {
private static Singleton instance;
/**
* 构造方法为private,堵死了外界利用new 创建实例的可能性
*/
private Singleton(){
}
//该方法是获得本类实例的唯一全局访问点
public static Singleton GetInstance(){
if(instance == null){ //若实例不存在,则new一个新实例,否则返回已有的实例
instance = new Singleton();
}
return instance;
}
}
客户端
package singleton;
public class Clinet {
public static void main(String[] args) {
Singleton t1 = Singleton.GetInstance();
Singleton t2 = Singleton.GetInstance();
if(t1 == t2){
System.out.println("两个对象是相同的实例");
}
}
}
多线程的单例
懒汉模式下的单例没有考虑线程安全的问题,在多线程下是线程不安全的,多个线程同时访问Singleton类,调用GetInstance()方法,会有可能创建多个实例
此时应该创建一个进程锁synchronized,,synchronized可以确保当一个线程位于代码的临界区时,另外的线程不进入临界区。其他线程试图进入锁定的代码时,它会一直等待,直到该对象被释放。
GetInstance()方法加同步锁
public static synchronized Singleton GetInstance(){
if(instance == null){ //若实例不存在,则new一个新实例,否则返回已有的实例
instance = new Singleton();
}
return instance;
}
双重锁
同步锁保护,固然保证单例,但是造成性能低,因此可以使用双重加锁检查机制
双重加锁机制:方法GetInstance()前不加同步锁,进入方法后,先检查实例是否存在,之后再进入加锁的同步块,进入同步块后,再检查实例是否存在,根据情况来判断是否创建对象,这样减少了多次在同步情况下加锁所消耗的时间。
public static Singleton GetInstance(){
if(instance == null){
synchronized (Singleton.class) {
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
饿汉模式
// 饿汉模式
public class Singleton {
// 私有构造
private Singleton() {}
private static Singleton instance = new Singleton();
// 静态工厂方法
public static Singleton getInstance() {
return instance;
}
}
饿汉模式的单例在类被加载的时候就被创建,保证了单列,而且本身是线程安全的
懒汉模式和饿汉模式的区别:懒汉模式是延时加载,是在需要的时候才创建对象,而饿汉模式在虚拟机启动的时候就会创建,饿汉模式无需关注多线程问题