您的位置:首页 > 其它

单例模式

2016-05-21 20:02 274 查看
单例模式,在网络上已经有很多说明,我写这篇博客,主要是为了整理网络上的说法,以及方便后期查看。

单例模式:主要是保证一个类仅有一个实例,并提供一个访问它的全局访问点。 也就是在同一个应用程序中,某个类

的实例只有一个,且只有一个公共的全局访问点。

单例模式的特点:在整个应用程序中,类的实例只有一个,且类的实例应该由类本身来负责产生,例外就是这个单例

必须要提供一个对外的口子,让其他对象可以访问该实例。

单例模式的写法大体分为三种:饿汉,懒汉,登记式,但是我这里同样也只是记录最常用的前两种。

单例模式的结构图:



Singleton类,定义一个getInstance(),允许客户访问它的唯一实例。该方法是一个静态方法,主要负责创建自己

的唯一实例。

懒汉式单例:

package com.my.singleton;

/**
* 创建一个懒汉式单例
*
* @author Administrator
*
*/
public class Singleton {

private static Singleton instance;

/**
* 构造私有,防止其他类可以实例化本类
*/
private Singleton() {
// TODO Auto-generated constructor stub
}

/**
* 此方法是其他对象访问该实例的唯一全局入口
*
* @return
*/
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}

}


所谓懒汉单例模式,主要是指:单例模式的处理方式上在第一次被引用时,才会将自己实例化。而饿汉单例模式,却

是在自己在被加载的时候就将自己实例化。

以上的懒汉单例模式,没有考虑多线程的问题,所以是线程非安全性的,当多线程并发访问该类时,可能就同时创建

很多实例。对于懒汉单例的线程安全性上,通常有2种较为常见的设计。

第一种,在getInstance()方法上加同步锁。如:

package com.my.singleton;

/**
* 创建一个懒汉式单例
*
* @author Administrator
*
*/
public class Singleton {

private static Singleton instance;

/**
* 构造私有,防止其他类可以实例化本类
*/
private Singleton() {
// TODO Auto-generated constructor stub
}

/**
* 此方法是其他对象访问该实例的唯一全局入口
*
* @return
*/
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}

}


这种方式确实是可以解决多线程并发访问的问题,但是性能上较差。在getInstance()方法上加锁,那么每一个并发访

问的线程都会一次一次的上锁,访问一次上锁一次,性能低下。

那么一般就修改成下面这种,双重检查:

package com.my.singleton;

/**
* 创建一个懒汉式单例
*
* @author Administrator
*
*/
public class Singleton {

private static Singleton instance;

/**
* 构造私有,防止其他类可以实例化本类
*/
private Singleton() {
// TODO Auto-generated constructor stub
}

/**
* 此方法是其他对象访问该实例的唯一全局入口
*
* @return
*/
public static Singleton getInstance() {
if (instance == null) {
synchronized (<pre name="code" class="java">Singleton.class


) {if (instance == null) {instance = new Singleton();}}}return instance;}}


这种方式就很常见了,在多线程操作同一个实例时。当然你可能会问,我们在getInstance()为什么要写2次if(instance

== null)的判断。首先,第一个判断,主要是为了判断实例是否为空,如果不为空,那么就压根不用进入到锁的块中

去。只有当实例为空时,才会进入到锁中去。既然是多线程,那么当多线程都执行到锁这个地方时,每次只能有1根

线程可以操作。如果没有第2个if判断,那么极有可能第1个线程创建一个实例,第2个线程依旧会创建一个实例。第3

个线程……

饿汉式单例:

package com.my.singleton;

/**
* 创建一个饿汉式单例
*
* @author Administrator
*
*/
public class Singleton {

private static Singleton instance = new Singleton();

/**
* 构造私有,防止其他类可以实例化本类
*/
private Singleton() {
// TODO Auto-generated constructor stub
}

/**
* 此方法是其他对象访问该实例的唯一全局入口
*
* @return
*/
public static Singleton getInstance() {
return instance;
}
}


饿汉式在类加载的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生就是线程安全的。

当然饿汉式单例除了上述写法外,还有一种静态内部类方式,如下:

package com.my.singleton;

/**
* 创建一个饿汉式单例
*
* @author Administrator
*
*/
public class Singleton {

/**
* 定义个懒加载类
*
* @author Administrator
*
*/
private static class LazyHolder {
private static final Singleton INSTANCE = new Singleton();
}

/**
* 构造私有,防止其他类可以实例化本类
*/
private Singleton() {
// TODO Auto-generated constructor stub
}

/**
* 此方法是其他对象访问该实例的唯一全局入口
*
* @return
*/
public static final Singleton getInstance() {
return LazyHolder.INSTANCE;
}
}


饿汉的这2种,都避免了线程安全上的问题,又避免了性能上的问题,所以使用较多。

例外的那种登记式单例模式,有兴趣的可以去网上查查资料看下,个人感觉用的较少,所以不在这里过多的描述。

总结来说就是:饿汉是在加载是就初始化了实例,天生线程安全。而懒汉是在调用式才去实例化,要做到线程安全需

要采用到synchronized 完成加锁。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: