一、概念
保证一个类仅有一个实例,并提供一个访问他的全局访问点。
二、模式动机
对一些全局性资原或者配置进行统一管理,以提供一个唯一的访问点,并减少内存的使用。
三、模式的结构
单例模式有如下特点:
1.单例类只能有一个实例
2.单例类必须自已创建自已的实例
3.单例类必须提一个访问点,以便向外部提供自已唯一的实例
模式示意代码
1.懒汉式
1 public class LazySingleton {
2
3 //定义一个变量用来存储创建好的唯一实例
4 private static LazySingleton instance = null;
5
6 //私有化构造函数,避免外面用new 直接创建任意多的实例
7 private LazySingleton() {
8
9 }
10
11 //定义一个静态方法,提供全局访问点
12 public synchronized static LazySingleton getInstance() {
13 //判断唯一实例是否已创建
14 if (instance == null) {
15 //如果没创建,则创建实例
16 instance = new LazySingleton();
17 }
18 //返回创建好的唯一实例
19 return instance;
20 }
21 }
分析:
a.private构造子,保证了外部不能用new直接创建该类的实例
b.getInstance用synchronized,解决了当多线程同步时,同时只能有一个线程进入该方法,从而避免了两个线程同时进入if块内部创建不同的instance实例
c.由于getInstance是静态方法(属于类的静态方法只能调用属于类的静态成员),所以那个private的instance成员必须是static
d.由于getInstance进行了synchronized,所以当多线程时会出现性能问题,且获取实例前,每次都要判断实例是否存在。
2.饿汉式
1 public class EagerSingleton {
2
3 //定义一个变量用来存储创建好的唯一实例 ,类加载时直接创建,且只创建一次
4 private static EagerSingleton instance = new EagerSingleton();
5
6 //私有化构造函数,避免外面用new 直接创建任意多的实例
7 private EagerSingleton() {
8
9 }
10
11 //定义一个静态工厂方法,提供全局访问点
12 public static EagerSingleton getInstance() {
13 //直接返回类加载时已创建好的实例
14 return instance;
15 }
16
17 }
分析:
a.private构造子,保证了外部不能用new直接创建该类的实例
b.静态成员 instance在类加载时直接实例化,因为虚拟机保证只会装载一次,在装载类的时候是不会发生并发的,所以饿汉式是线程安全的。试想一下,如果饿汉式的instance如果不是static会出现什么情况,会出现死循环。
c.由于静态成员并非是在使用实例时实例化,而是在类加载时就实例化,所以饿汉式是空间换时间的一种模式。
四、模式样例
系统中属性文件的读取
1 import java.io.File;
2 import java.io.FileInputStream;
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.util.Properties;
6
7 public class PropertiesManager {
8 private static String fileName="D:\\workspace\\exec\\bin\\singleton\\app_base.properties";
9 private Properties props=new Properties();
10 private static PropertiesManager instance=new PropertiesManager();
11
12 private PropertiesManager() {
13 readFile();
14 }
15
16 private void readFile() {
17 InputStream in=null ;
18 try {
19 in=new FileInputStream(new File(fileName));
20 props.load(in);
21 } catch (IOException e) {
22 e.printStackTrace();
23 }finally{
24 try {
25 in.close();
26 } catch (IOException e1) {
27 // TODO Auto-generated catch block
28 e1.printStackTrace();
29 }
30 }
31 }
32
33 public static PropertiesManager getInstance() {
34 return instance;
35 }
36 public String getItem(String key) {
37 return props.getProperty(key);
38 }
39
40 }
使用方法如:
import singleton.PropertiesManager;
public class TestC {
public static void main(String[] args) {
PropertiesManager obj=PropertiesManager.getInstance();
System.out.println(obj.getItem("name"));
}
}
五、模式的约束
同一个JVM中会多个类加载器,当两个类加载器同时加载同一个类时,会出现两个实例,这样也意味着单例类也有可能有多个实例,这样就有可能会违背了单例类的初衷,更何况随着系统的变大,还会用到集群,所以单例类现在使用的场景可能越来越少,或者我们使用单例类的目的已发生了变化,常常是为了减少内存的使用而使用单例类。
六、模式的变体与扩展
多例模式(暂略)
七、与其它模式的关系
单例类的全局访问点用的就是静态工厂模式模式
八、模式优缺点
1.懒汉模式:在类加载时并不会创建唯一实例,只有在调用getInstance时才会创建唯一实例,但时在每次获取实例时又会涉及多线程同步的问题,且每次都要判断实例是否已创建,所以懒汉模式是典型的以时间换空间(只有获取实例时才创建对像)。
2.饿汉模式:在类加载时就实例化,在调用全局方问点时,直接返回已创建好的实例,所以速度要比懒汉模式快一些,是空间换时间的一种模式
下面的例子弥补了以上两种模式的不足
样例:
public class Singleton {
private static class SingletonHolder{
private static Singleton instance=new Singleton();
}
p