1.定义
保证一个类,在运行期间只有一个实例对象。并提供一个访问他的全局访问点。
结构与说明
Singleton:负责创建Singleton类自己的唯一实例,并提供一个getInstance方法,让外部访问这个类的唯一的实例。
2.代码示例
1:懒汉式
1 package com.singleton.example1; 2 /** 3 * 懒汉式单例模式示例 4 * @author admin 5 * 6 */ 7 public class Singleton { 8 //定义一个变量用来存储创建好的类的实例 9 private static Singleton uniqueInstance=null; 10 //私有化构造方法 11 private Singleton(){ 12 13 } 14 /** 15 * 定义一个方法来为外部提供类实例 16 * @return 17 */ 18 public static synchronized Singleton getInstance(){ 19 //如果uniqueInstance不存在,就新创建一个 20 if(uniqueInstance==null){ 21 uniqueInstance=new Singleton(); 22 } 23 //如果存在,则直接使用 24 return uniqueInstance; 25 } 26 27 }
2:饿汉式
1 package com.singleton.example2; 2 /** 3 * 饿汉式单例模式示例 4 * @author admin 5 * 6 */ 7 public class Singleton { 8 /** 9 * 定义一个变量来存储定义好的类实例,直接在这里创建类示例,只会创建一次 10 */ 11 private static Singleton uniqueInstance=new Singleton(); 12 /** 13 * 私有化构造方法,好在内部控制创建实例的数目 14 */ 15 private Singleton(){ 16 17 } 18 19 /** 20 * 定义一个方法来为外部提供实例 21 */ 22 public static Singleton getInstance(){ 23 //直接使用创建好的实例 24 return uniqueInstance; 25 } 26 }
3.体会单例模式
场景:现在需要在程序运行过程中读取配置文件
1:不用设计模式的话,如果在系统中存在多处需要使用配置文件的信息时候,如果每次都调用读取配置信息类的构造方法产生新的实例的话,这样会造成内存的严重浪费,是比较损耗资源的。
2:但是如果我们使用单利模式的话,只在系统运行需要读取配置文件的第一次进行实例化读取类的,然后将配置信息保存下来,那么剩余的其他的地方需要的时候我们直接使用以及读取出来的配置信息就可以了,这样在系统运行期间只会产生一个读取配置文件类的实例,相比来说占用了很少的系统资源。
代码示例如下:
1 package com.singleton.example3; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.util.Properties; 6 7 /** 8 * 使用饿汉式单例模式来读取配置文件 9 * @author admin 10 * 11 */ 12 public class AppConfig { 13 /** 14 * 定义一个变量来存储类实例,直接在这进程创建,只创建一次 15 */ 16 private static AppConfig instance=new AppConfig(); 17 18 private String parameterA; //参数A 19 private String parameterB; //参数B 20 21 public String getParameterA() { 22 return parameterA; 23 } 24 public String getParameterB() { 25 return parameterB; 26 } 27 /** 28 * 私有化构造方法 29 */ 30 private AppConfig(){ 31 readConfig(); 32 } 33 /** 34 * 提供一个外部访问实例的接口 35 * @return 36 */ 37 public static AppConfig getInstance(){ 38 return instance; 39 } 40 41 private void readConfig(){ 42 System.out.println("读取一次配置文件======="); 43 Properties p=new Properties(); 44 InputStream in=null; 45 try{ 46 in=AppConfig.class.getResourceAsStream("AppConfig.properties"); 47 p.load(in); 48 this.parameterA=p.getProperty("paramA"); 49 this.parameterB=p.getProperty("paramB"); 50 }catch(IOException e){ 51 System.out.println("读取配置文件出错,具体堆栈信息如下:"); 52 e.printStackTrace(); 53 }finally{ 54 try{ 55 in.close(); 56 }catch(IOException e){ 57 System.out.println("读取配置文件出错,具体堆栈信息如下:"); 58 e.printStackTrace(); 59 } 60 } 61 } 62 }
外部调用代码如下:
1 package com.singleton.example3; 2 3 public class Client { 4 public static void main(String[] args) { 5 for(int i=0;i<5;i++){ 6 AppConfig ac=AppConfig.getInstance(); 7 System.out.println("i=="+i+" paramA="+ac.getParameterA()+" paramB="+ac.getParameterB()); 8 } 9 } 10 }
运行结果如下:
读取一次配置文件=======
i==0 paramA=a1 paramB=b1
i==1 paramA=a1 paramB=b1
i==2 paramA=a1 paramB=b1
i==3 paramA=a1 paramB=b1
i==4 paramA=a1 paramB=b1
4.认识单例模式
1:单例模式的功能
保证一个类在运行期间只有一个实例,同时提供一个外部访问实例的访问点。
2:单例模式的范围
是一个ClassLoader及其子ClassLoader的范围。
3:单利模式的命名
一般建议单例模式的方法命名为:getInstance()。
5.理解单例模式
1:不加同步的懒汉式是线程不安全的。
2:恶汉式是线程安全的,因为虚拟机保证了只会加载一次。
3:如何实现懒汉式的线程安全呢?
加上synchronized即可。
4:双重检查加锁
所谓双重检查加锁机制,指的是:并不是每次进入getInstance方法的时候都进行同步,而是先不同步,进入方法过后,先检查实例是否存在,如果不存在才进入下面的同步模块,这是第一重检查。进入同步模块后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查,这样就只同步一次了,减少了多次同步造成的时间资源浪费。
双重检查加锁机制的实现会使用一个关键字volatile,它的意思是:被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理变量。
注意:java1.4及以前的版本中,很多JVM对volatile关键字的实现有问题,会导致双重检查加锁的失败,因此本机制只能用在java5及以上版本。
双重检查加锁机制的代码示例如下:
1 package com.singleton.example4; 2 /** 3 * 双重检查加锁机制实现懒汉式单例模式 4 * 该机制只能用在Java5.0及以上版本 5 * @author admin 6 * 7 */ 8 public class Singleton { 9 /** 10 * 对保存实例的变量使用volatile关键字修饰 11 */ 12 private volatile static Singleton instance=null; 13 /** 14 * 私有化构造方法,不让外部直接进行实例化对象操作 15 * 同时在内部控制实例化对象的数目 16 */ 17 private Singleton(){ 18 19 } 20 /** 21 * 提供给外部访问类实例的方法 22 * @return 返回类实例 23 */ 24 public static Singleton getInstance(){ 25 //先检查实例是否存在,如果不存在才进入下面的同步块 26 if(instance==null){ 27 //同步块,线程安全的创建实例 28 synchronized(Singleton.class){ 29 //再次检查实例是否存在,如果不存在才真的创建实例 30 if(instance==null){ 31 instance=new Singleton(); 32 } 33 } 34 } 35 return instance; 36 } 37 38 public static void main(String[] args) { 39 for(int i=0;i<3;i++){ 40 //输出类的HashCode 41 System.out.println(Singleton.getInstance()); 42 } 43 } 44 }
输出结果为:
com.singleton.example4.Singleton@18a992f
com.singleton.example4.Singleton@18a992f
com.singleton.example4.Singleton@18a992f