一、概念
保证一个类仅有一个实例,并提供一个访问他的全局访问点。
二、模式动机
对一些全局性资原或者配置进行统一管理,以提供一个唯一的访问点,并减少内存的使用。
三、模式的结构
单例模式有如下特点:
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(); } private Singleton() { } public static Singleton getInstance() { return SingletonHolder.instance; } public void println() { System.out.println("Singleton"); } }
使用方法:
1 public class TestC { 2 3 Singleton singleton=Singleton.getInstance(); 4 singleton.println(); 5 6 } 7 }