一、『单件模式』定义
确保一个类只有一个实例,并提供一个全局访问点。
单件模式即单例模式。
使用场景很多,比如多个程序都要使用一个配置文件中的数据,而且要实现数据共享和交换。必须要将多个数据封装到一个对象中,而且多个程序操作的是同一个对象,也就是说必须保证这个配置文件对象的唯一性。
有两种方式实现:饿汉式和懒汉式。
二、使用
2.1 饿汉式
单件模式的急切实例化方式,上来就创建对象:
class Single {
//2,创建一个本类对象。
private static Single uniqueInstance = new Single();
//1,私有化构造函数。禁止程序创建该类的对象。
private Single() {
}
//3,定义一个方法返回这个对象。该方法加上public,权限最大。
public static Single getInstance() {
return uniqueInstance;
}
}
/**
* 演示
*/
public class Demo {
public static void main(String[] args) {
// 调用getInstance方法获取Single的对象。
Single ss1 = Single.getInstance();
Single ss2 = Single.getInstance();
// ss1和ss2操作的是同个对象,因为地址相同
System.out.println(ss1 == ss2);
}
}
如果创建的这个单件对象一直没用到过,而且该对象也一直比较占用空间,那么饿汉式并不是很完美的解决方法。
2.2 懒汉式
单例的延迟实例化方式,用到才创建对象:
class Single {
//2,创建一个本类对象。
private static Single uniqueInstance = null;
//1,私有化构造函数。禁止程序创建该类的对象。
private Single() {
}
//3,定义一个方法返回这个对象。该方法是关键,加上public,权限最大。
public static Single getInstance() {
if (uniqueInstance == null)
uniqueInstance = new Single();
return uniqueInstance;
}
}
/**
* 演示
*/
public class Demo {
public static void main(String[] args) {
// 调用getInstance方法获取Single的对象。
Single ss1 = Single.getInstance();
Single ss2 = Single.getInstance();
// ss1和ss2操作的是同个对象,因为地址相同
System.out.println(ss1 == ss2);
}
}
2.3 小细节
(1)私有化构造函数是为了禁止其它程序创建该类的对象,保证只有该类内部才能创建该类的对象。
(2)想获取Single类的对象就需要调用getInstance方法。既然无法通过对象调用,那么只能用类名调用,所以getInstance方法必须是static。
(3)创建好对象后,因为uniqueInstance对象是静态的,所以通过Single.uniqueInstance这种方式是可以获取对象的,但不推荐,用方法访问是为了对对象可控,因此对象需加上private。
三、单件模式的多线程安全问题
对比饿汉式和懒汉式两种单件模式的实现,饿汉式没有多线程安全问题,但是懒汉式是有多线程安全问题的。
3.1 懒汉式分析一
初始的饿汉式实现:
class Single {
//2,创建一个本类对象。
private static Single uniqueInstance = null;
//1,私有化构造函数。禁止程序创建该类的对象。
private Single() {
}
//3,定义一个方法返回这个对象。该方法是关键,加上public,权限最大。
public static Single getInstance() {
if (uniqueInstance == null)
uniqueInstance = new Single();
return uniqueInstance;
}
}
问题:假设现在uniqueInstance对象还是null,然后有多个线程同时调用getInstance()方法想获得uniqueInstance对象,可能会出现多个线程都判断uniqueInstance为空,接着就重复new了对象,这里就出现多线程安全问题了。
3.2 懒汉式分析二
上面分析发现:并发访问会有安全隐患,所以加入同步机制解决安全问题。
class Single {
//2,创建一个本类对象。
private static Single uniqueInstance = null;
//1,私有化构造函数。禁止程序创建该类的对象。
private Single() {
}
//3,定义一个方法返回这个对象。该方法是关键,加上public,权限最大。
public static Single getInstance() {
synchronized (Single.class) {
if (uniqueInstance == null)
uniqueInstance = new Single();
}
return uniqueInstance;
}
}
问题:只有第一次执行此方法时,才真正需要同步。换句话说,一旦创建好uniqueInstance对象,就不再需要同步这个方法了。之后每次调用这个方法,同步都是一种累赘。
3.3 懒汉式分析三
上面分析发现:同步的出现降低了效率。可以通过双重判断的方式,解决效率问题,减少判断锁的次数。
class Single {
//2,创建一个本类对象。
private volatile static Single uniqueInstance = null;
//1,私有化构造函数。禁止程序创建该类的对象。
private Single() {
}
//3,定义一个方法返回这个对象。该方法是关键,加上public,权限最大。
public static Single getInstance() {
if(uniqueInstance == null) {
synchronized (Single.class) {
if (uniqueInstance == null)
uniqueInstance = new Single();
}
}
return uniqueInstance;
}
}
volatile关键词确保:uniqueInstance变量被初始化成Single实例时,多个线程正确地处理uniqueInstance变量。
参考
《HeadFirst设计模式》