初识单例模式:
定义:保证一个类仅有一个实例,并提供一个访问他的全局访问点。
理解单例模式:
1.单例模式功能:单例模式的功能是用来保证这个类在运行期间只会被创建一个实例,并提供一个全局唯一访问这个类实例的访问点。
2.单例模式的范围:是一个classLoader及其子classLoader的范围;
3.单例模式的命名:一般建议单例模式的方法命名为getInstance();
**单例模式的实现方式(一)**:
//**懒汉模式**
public class Singleton{
//4.定义一个变量用来存储创建好的实例
//5.因为这个变量要在静态方法中使用,所以需要加上static来进行修饰
private static Singleton instance=null;
//1.私有化构造方法,好在内部进行控制创建实例的数目
private Singleton(){};
//2.定义一个方法来为客户端提供实例
//3.这个方法要定义成类方法,也就是要加上static关键字
public static synchronized Singelton getInstance(){
//6.判断存储实例的变量是否有值
if(instance==null){
//6.1如果没有值,就创建一个实例,并把这个赋值给存储类型的变量
instance = new Singleton();
}
//6.2如果有值就直接使用
return instance;
}
}
//饿汉模式
public class Singleton{
//4.定义一个变量用来存储创建好的类实例,直接在这里创建类实例,
//直接在这里创建实例,由虚拟机保证只会创建一次。
private static Singleton instance=new Singleton();
//1.私有化构造方法,控制创建实例的数目
private Singleton(){};
//2.定义一个方法用来为客户端提供类实例
//3.这个方法要定义成类方法,也就是要加上static
public static Singelton getInstance(){
//5.直接使用已经创建好的实例
return instance;
}
}
单例模式的实现方式(二):
- 双重检测加锁(懒汉模式):双重检测加锁指的是并不是每次进入getInstance()方法都需要加锁,而是先不加锁,进入方法之后,先检查实例是否存在,如果不存在才进入同步块中,这是第一重检查。进入代码块后,再次检查实例是都存在,如果不存在,就在同步的情况下创建一个实例。这是第二重检查,这样一来,就只需要同步一次,从而减少了多次同步情况下进行判断所浪费的时间。
- 双重加锁机制的实现会使用一个关键字volatile,他的意思就是:被volatile修饰的变量值,将不会被本地线程缓存,所有对该变量的读写,都是直接操作共享内存,从而保证多个线程能正确的处理该变量。
public class Singleton{
//对保存实例的变量添加volatile进行修饰
private volatile static Singleton instance=null;
private Singleton(){};
public static Singleton getInstance(){
//先检查实例是否存在,如果不存在才进入下面的同步块中
if(instance==null){
synchronized(Singleton.class){
//再次检查实例是否存在,如果不存在才真正的创建实例
if(instance==null){
instance = new Singleton();
}
}
}
return instance;
}
}
单例模式的实现方式(三):
- 内部类的方式(饿汉模式):这个模式综合使用了java中的类级内部类和多线程缺省同步锁的知识,很巧妙的同时实现了延迟加载和线程安全。
public class Singleton{
//类级的内部类,也就是静态的成员内部类,该内部类的实例和外部类的实例没有绑定关系
private static class SingletonHolder{
//静态初始化器,由jvm保障线程安全
private static Singleton instance=new Singleton();
}
//私有化构造方法
private Singleton(){};
private static Singleton getInstance(){
return SingletonHolder.instance;
}
}
单例模式的实现方式(四):
根据枚举类的特性来实现单例模式,这里不做代码实现;
理解单例模式:
单例模式体现的思想:
- 延迟加载的思想
通俗点说就是一开始不要加载资源或者数据,一直等到需要使用这个资源或者数据了,躲不过去了才加载,所以也称为Lazy Load,不是懒惰,是“延迟加载”,这在实际开发中是一种很常见的思想,尽可能的节约资源。 - 缓存的思想
单例模式中的懒汉模式还体现了缓存的思想,缓存也是在实际开发中非常常见的功能。
简单讲就是,如果某些资源或者数据会被频繁的使用,可以把这些数据缓存在内存中,每次操作的时候,先在内存里面找,看有没有这些数据,如果有那么就直接使用,如果没有,那么就获取它,并设置到缓存中,下一次访问的时候就可以直接从内存中获取它。从而节省了大量的时间,当然,缓存是一种典型的空间换时间的方案。
单例模式的优缺点: - 时间和空间:懒汉模式是典型的时间换空间,饿汉模式是典型的空间换时间。
- 线程安全:不加同步锁的懒汉单例模式是线程不安全的,饿汉模式是线程安全的,因为虚拟机保证了只会装载一次。
**单例模式思维拓展(不在是单例模式,而是三例模式):
//简单演示如何扩展单例模式,控制实例数目为3(线程不安全)
public class OneExtend{
//定义一个缺省的key值的前缀
private final static String DEFAULT_PREKEY="cache";
//缓存实例的容器
private static Map<String,Object> map= new HashMap<String,Object>();
//用来记录正在使用的是第几个实例
private static int num=1;
//定义控制实例的最大数目
private final static int NUM_MAX=3;
private OneExtend(){};
private static OneExtend getInstance(){
String key=DEFAULT_PREKEY+num;
OneExtend oneExtend = map.get(key);
if(oneExtend == null){
oneExtend = new OneExtend();
map.put(key,oneExtend);
}
//把当前实例的序号加1
num++;
if(num > NUM_MAX){
//如果实例的序号超过了最大数目,那就重复从1开始获取
num=1;
}
return oneExtend ;
}
}