单例模式
单例模式的核心结构只有一个单例类,单例模式要保证这个类在运行期间只能被实例化一次,即只会被创建唯一的一个单例类的实例。
单例模式需要提供一个全局唯一能得到这个类实例的访问点,一般通过定义一个名称类似为GetInsrance的公用方法实现这一目的。
要满足上面的两点要求,应该很容易想到:
1、该类的构造函数应该是私有的,不能随意被实例化是保证只有一个实例的前提。
2、该类需要提供一个公开的且返回值类型为单例类类型的公用方法。
(单例类只能有一个实例,单例类必须自己创建自己的唯一实例,单例类必须给所有其他对象提供这一实例)
01、单例模式应用场景
单例模式应用于各种系统中某个类的对象只能存在一个类似场景,举个例子:教室里面的教师和学生都是需要在黑板上写字,但是一般情况下,教室里边应该只有一个黑板,它是教师和学生公用的,这时就要想办法保证取得的黑板是一个共享的唯一的对象,而单例模式就是解决这类问题的一个已经成型的模式
02、如何实现单例模式
通常有2种方式:懒汉式 和 饿汉式。
饿汉式: java代码
1 Public class Singleton 2 { 3 Private static final Singleton singleton = new Singleton(); 4 Private Singleton() 5 { 6 7 } 8 Public static Singleton getInstance() 9 { 10 return singleton; 11 } 12 }
采用饿汉式方法实现单例模式的方法是在类的内部实例化一个静态变量singleton,这是比较好理解的,既然一个类要满足上边的几个条件,那么很自然的想到声明一个类的静态变量,天生是线程安全的。还要注意的是该类的构造方法是私有的,这样类就不提供默认的构造函数,因此也就不能实例化了
懒汉式:java代码:
1 Public class Singleton 2 { 3 Private static final Singleton singleton; //未初始化 4 Private Singleton() 5 { 6 } 7 Public static Singleton getInstance() 8 { 9 if(singleton == null) 10 { 11 Singleton = new Singleton(); 12 } 13 } 14 }
懒汉式也是通过一个类的静态变量来实现的,但是并没有直接初始化,而是在函数getInstance()中实例化的,也就是每次想用这个实例的时候初始化,如果已经初始化,那么就不用再初始化了,这种方式也是常用的一种
03、多线程下的单例模式
当然,在多线程的情况下,上述方法会有问题。对于懒汉式实现方法,如果现在存在着线程A和B,代码执行情况是这个样子的,线程A执行到了 if(singleton == null),线程B执行到了Singleton = new Singleton();线程B虽然实例化了一个Singleton,但是对于线程A来说判断singleton还是没有初始化的,所以线程A还会对singleton进行初始化,这样就会出现问题。解决的办法就是要用到Synchronized(同步)。java中提供了2种同步方式: 同步方法 和 同步声明。
因此,Public static Singleton getInstance() 加上 一个Sychronized就好了。
加上 Public static Sychronized Singleton getInstance() 后,当线程B访问这个函数的时候,其他任何要访问该函数的代码不能执行,直到线程B执行完该函数(利用锁实现)。
但是多个线程访问同一个函数的时候,那么只能有一个线程能够访问这个函数,这样显然效率很低,因此用另外一种方式实现:代码如下
1 Public static Singleton getInstance() 2 { 3 if(singleton == null) 4 { 5 Synchronized(singleton.class) 6 { 7 Singleton = new Singleton(); 8 } 9 } 10 }
这种方式将在方法中的声明转移到内部代码块中,只有当 singleton == null 时,才需要锁机制,但是如果线程A和B同时执行到了Synchronized(singleton.class),虽然也只有一个线程能够执行,假如线程B先执行,线程B获得锁,线程B执行完之后,线程A获得锁,但是此时没有检查singleton是否为空就执行了,所以还会出现两个singleton实例的情况,于是就需要双重检查模式(DLC),代码如下:
1 Public static Singleton getInstance() 2 { 3 if(singleton == null) 4 { 5 Synchronized(singleton.class) 6 { 7 if(singleton == null) 8 { 9 Singleton = new Singleton(); 10 } 11 } 12 } 13 }
注意:多线程访问全局变量的时候会影响数据的一致性