设计模式之单例模式
单例模式属于创建型模式,创建型模式帮助我们创建对象,对象有简单有复杂的。
单例模式的概念十分简单:保证一个类只有一个实例(对象),并且提供一个开放的方法访问该实例(对象)。虽然概念简单,但是其应用场景确实十分的广泛。例如:
- spring中,每个Bean默认是单例的。
- 数据库连接池的设计也是采用的单例模式。
- 在servlet编程中,每个servlet也是单例的。
- ……
下面介绍5中常见的单例模式实现方式。
饿汉式
/**
* 饿汉式单例模式
* @author KKSJS
*
*/
public class SingletonDemo01 {
//提供一个私有的static成员变量
private static SingletonDemo01 singletonDemo01 = new SingletonDemo01();
//将构造器私有化
private SingletonDemo01(){}
//提供一个唯一的公共方法,将上面的变量返回给调用者
public static SingletonDemo01 getInstance(){
return singletonDemo01;
}
}
public class TestSingleton {
public static void main(String[] args) {
System.out.println(
SingletonDemo01.getInstance() == SingletonDemo01.getInstance());
}
}
打印结果为true,说明单例模式创建成功。
在饿汉式的单例模式代码中,singletonDemo01这个变量会在类加载时初始化。虚拟机保证只加载一次这个类,所以不存在并发访问的问题。
因为singletonDemo01属性是静态的,所以会立即加载,当类加载器加载SingletonDemo01这个类的时候,就把这个对象new出来初始化给singletonDemo01变量,不管需不需要使用这个对象,都会new出来,所以称之为饿汉式。对于饿汉式,如果没有调用getInstance()
方法,就会造成资源浪费,因为类的对象无论如何都会被创建。
懒汉式
/**
* 懒汉式单例模式
* @author KKSJS
*
*/
public class SingletonDemo02 {
private static SingletonDemo02 singletonDemo02;
private SingletonDemo02(){}
public static synchronized SingletonDemo02 getInstance(){
if (singletonDemo02==null) {
singletonDemo02 = new SingletonDemo02();
}
return singletonDemo02;
}
}
这种单例模式称之为懒汉式。懒汉式实现了延迟加载,所谓的延迟加载指的是当我们调用getInstance()
方法时才会创建对象,这样的话资源利用率得到的提高。但是随之而来的问题是调用效率变低了,因为我们在getInstance()
方法上加上了synchronized关键字。
因为getInstance()
这个方法有可能多个线程都在调用,所以要加一个synchronized关键字,避免在并发量高的时候创建多个对象的问题。那么如果不加这个关键字会出现什么问题呢?
假设某一个线程A刚执行完if判断,即被挂起。线程B进行if判断后,执行了s=new SingletonDemo01();随即B又被挂起,然后A又从被挂起的地方执行,又new了一个对象,这样就创建出来了两个对象。这与单例模式的初衷是相违背的。
静态内部类实现
/**
* 静态内部类实现单例模式
* @author KKSJS
*
*/
public class SingletonDemo04 {
private SingletonDemo04(){}
private static class SingletonClassInstance{
private static SingletonDemo04 singletonDemo04 = new SingletonDemo04();
}
public static SingletonDemo04 getInstance(){
return SingletonClassInstance.singletonDemo04;
}
}
这种方式由于外部类没有static属性,不会立即加载,所以资源利用率较高。只有真正调用getInstance()
方法后才会初始化静态内部类,才会创建对象,所以实现了懒加载,即资源利用率高。
双重检测锁实现
/**
* 双重检测锁式单例模式
* @author KKSJS
*
*/
public class SingletonDemo03 {
private static SingletonDemo03 singletonDemo03 = null;
private SingletonDemo03(){}
public static SingletonDemo03 getInstance(){
if(singletonDemo03 == null){
SingletonDemo03 temp;
synchronized (SingletonDemo03.class) {
temp = singletonDemo03;
if (temp == null) {
synchronized (SingletonDemo03.class) {
if (temp==null) {
temp = new SingletonDemo03();
}
}
singletonDemo03 = temp;
}
}
}
return singletonDemo03;
}
}
这种方式比较复杂,不是很常用。这种模式将同步内容下放到if内部,不必每次获取对象时都同步,只有第一次才同步。由于JVM底层模型原因,这种方式偶尔会出问题,不推荐使用。
枚举方式实现
/**
* 枚举实现单例模式
* @author KKSJS
*
*/
public enum SingletonDemo05 {
//定义一个枚举元素,它表示一个SingletomDemo05类的对象
singletonDemo05;
}
可以看到这种方式代码十分的简洁,由于枚举类天然具有单例属性,所以用来创建单例对象是最合适的。
public class TestSingleton {
public static void main(String[] args) {
System.out.println(
SingletonDemo05.singletomDemo05==SingletonDemo05.singletomDemo05);
}
}
经过测试,打印结果为true,说明这种方式是可行的,并且枚举方式是五种方式里面最简单的。但是这种方式唯一的问题就是没有实现懒加载,即无论我们使不适用,对象都会创建。