单例模式 Singleton
定义: 保证一个类只有一个实例,并提供一个访问它的全局访问点.
解决思路: 一个类能够创建多个实例问题的根源就在于类的构造方法是公开的,也就是可以通过类的外部通过构造方法来创建多个实例,换句话说,只要类的构造方法时公开的就没有办法控制类的外部创建类的实例的个数.
要想控制一个类只被创建一个实例,那么首要问题就是要把创建的权限收回来,让类的自身来负责创建类的实例,然后由类自身来负责自己类实例的创建工作,然后由这个类提供外部可以访问这个类的实例的方法,这就是单例模式的实现方法.
单例模式的说明:
在Java中,单例模式的实现方式可以分两种,一种称为饿汉式,一种称为懒汉式.其实就是在创建类的实例的处理上,有不同的实现方式.
懒汉式实现实例代码:
public classSingleton{
private static Singleton uniqueSingleton = null;
//私有化构造方法,可以在内部控制创建实例的数目
private Singleton(){//可以进行相应的处理功能}
public static synchronized Singleton getInstance(){
if(uniqueSingleton==null){
uniqueSingleton =new Singleton();
}
return uniqueSingleton;
}
//单例模式可以有自己的操作和属性
}
饿汉式实例代码
public classSingleton{
private static Singleton uniqueSingleton = new Singleton();
//私有化构造方法,可以在内部控制创建实例的数目
private Singleton(){//可以进行相应的处理功能}
public static synchronized Singleton getInstance(){
return uniqueSingleton;
}
//单例模式可以有自己的操作和属性
}
模式讲解:
认识单例模式:
(1)功能: 单例模式是用来保证这个类在运行过程中只被创建一个类实例,另外单例模式还提供了一个全局唯一访问这个类实例的访问点,就是getInstance()方法.
懒汉式单例的实现步骤(具体解释略)
1. 私有化构造方法(防止外部任意创建类实例)
private Singleton(){}
2. 提供获取实例的方法
public Singleton getInstance(){}
3. 将获取实例的方法设计成静态的
public static Singleton getInstance(){}
4. 定义存储实例的属性
private Singleton instance = null;
5. 把这个属性设置成静态的
private static Singleton instance =null;
6. 实现控制实例的创建
public static Singleton getInstatnce(){
if(instance ==null){
instance = newSingleton();
}
return instance;
}
饿汉式的实现
public classSingleton{
//定义一个静态变量来存储创建好的类实例
private static Singleton uniqueSingleton = new Singleton();
//1 私有化构造方法
private Singleton(){//可以进行相应的处理功能}
//2:定义一个方法来为客户端提供类实例
//3:将这个方法定义成类方法
public static synchronized Singleton getInstance(){
//返回创建好的类实例
return uniqueSingleton;
}
//单例模式可以有自己的操作和属性
}
延迟加载思想:
单例模式的懒汉式实现方式体现了延迟加载的思想,延迟加载就是一看是不要加载资源或者数据,一直等到马上就要使用这个资源或者数据了,躲不过去了才加载,所以也称作”Lazy Load”.在实际开发中,这是一种很常见的思想,尽可能的节约资源.
缓存思想:
单例模式的实现还体现了缓存的思想,缓存也是实际开发中常见的功能.
简单的说就是当某些资源或者数据被频繁的使用,而这些资源或者数据存储在系统外部,比如数据库,硬盘文件等,那么每次操作这些数据的时候都得从数据库或者硬盘上去获取,速度会很慢,将造成性能问题.
一个简单的解决方法就是:把这些数据缓存到内存中,每次操作的时候,先到内存里找,看有没有这些数据,如果有就直接使用,如果没有就获取它,并设置到缓存中,下次访问的时候就可以直接从内存中获取了,从而节省大量的时间.缓存时一种典型的空间换时间的方案
单例模式的优缺点:
1. 时间和空间
懒汉式是一种典型的时间换空间的,也就是每次获取实例的时候都得进行判断,看是否需要创建实例,浪费判断的时间,如果一直没有人使用,那就不用创建实例,则节约了内存空间.
饿汉式是典型的空间换时间,当类装载的时候就会创建类实例,不管你用不用,先创建出来,然后每次访问的时候就不需要在判断了,节约了运行时间.
2. 线程安全
从线程安全上讲,不加同步的懒汉式是线程不安全的,比如当两个线程同时调用getInstance()方法的时候就可能导致并发问题.
饿汉式是线程安全的,因为虚拟机保证只会加载一次,在装载类的时候是不会发生并发的.
如何实现懒汉式的线程安全:只要加上syhcronized即可,如public staticsynchronized Singleton getInstance(){},但是这样一来,会降低整个访问的速度,而且每次都要判断.
3. 双重检查枷锁
所谓双重检查枷锁指的是:并不是每次进入getInstance()方法都要进行同步,而是先不同步,进入方法之后,先检查案例是否存在,如果不存在才进入下面的同步块,这时第一重检查,进入同步块之后再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查,这样一来就只需要同步一次了,从而减少了多次在同步的情况下进行判断浪费的时间.
public class Singleton{
private valitile static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
if(instance== null){
synchronized(Singleton.class){
if(instance == null){
instance = newSingleton();
}
}
}
return instance;
}
}
提示:volatile关键字可能会屏蔽掉虚拟机中一些必要的代码优化,所以运行效率并不是很高.因此一般建议们没有特别的需要,不要使用.
在Java中一种更好的实现单例的方法,
public class Singleton {
/* 类级内部类,也就是静态的成员内部类,该内部类的实例与外部类的实例
* 没有绑定关系,而且只有被调用时才会加载,从而实现了延迟加载
*/
private static class SingletonHolder {
//静态初始化器,由JVM来保证线程安全
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return SingletonHodler.instance;
}
}
}
当getInstance()方法第一次被调用的时候,它第一次读取SingletonHolder.instance,导致SingletonHolder类得到初始化,而这个类在装载并被初始化的时候会初始化它的静态域,从而创建Singleton实例,由于是静态的域,因此只会在虚拟机装载类的是初始化一次,并由虚拟机保证它的线程安全.