单例模式的理解
一、什么是单例模式
单例模式的作用是保证应用程序在任何时刻只存在一个单例对象,比如打印机,一旦开始打印了就不再接收其他打印任务,一直到当前任务结束,否则会出现打印出的东西里存在多个任务中的信息;
二、单例的特征
[1] 单例只能有一个实例;
[2] 单例只能自己实例化唯一实例;
[3]单例必须向其他对象提供这一实例;
三、常见单例的模式
[1]饿汉式:不管其他对象需不需要单例的唯一实例,我都先完成实例化,你要就拿不用就放那;
1 package com.zyy.stop; 2 3 public class Zyyprotest { 4 5 public static void main(String[] args) { 6 SingleTon1.getSingleTon(); 7 } 8 } 9 10 class SingleTon1 { 11 12 private SingleTon1 () {} 13 14 //不管需不需要,先创建再说 15 static SingleTon1 st1 = new SingleTon1(); 16 17 public static SingleTon1 getSingleTon() { 18 return st1; 19 } 20 }
[2]懒汉式:外部需要唯一实例,不存在才会去创建;
1 package com.zyy.stop; 2 3 public class Zyyprotest { 4 5 public static void main(String[] args) { 6 SingleTon2.getSingleTon(); 7 } 8 } 9 10 class SingleTon2 { 11 12 private SingleTon2 () {} 13 14 15 static SingleTon2 st2 = null; 16 17 public static SingleTon2 getSingleTon() { 18 //不存在才会去创建 19 if (st2 == null) 20 st2 = new SingleTon2(); 21 22 return st2; 23 } 24 }
[3] 登记式:创建过唯一实例后,将该实例登记到Map中,下次用的直接从Map中获取;
1 package com.zyy.stop; 2 3 import java.util.Map; 4 5 import org.apache.commons.collections4.map.HashedMap; 6 7 public class Zyyprotest { 8 9 public static void main(String[] args) throws InstantiationException, IllegalAccessException, 10 ClassNotFoundException { 11 SingleTon3.getSingleTon("SingleTon3"); 12 } 13 } 14 15 class SingleTon3 { 16 17 private static Map<String, SingleTon3> map = new HashedMap<String, SingleTon3>(); 18 19 protected SingleTon3 () {} 20 21 public static SingleTon3 getSingleTon(String name) throws InstantiationException, IllegalAccessException, 22 ClassNotFoundException { 23 24 if (name == null) 25 name = SingleTon3.class.getName(); 26 27 if (map.get(name) == null) 28 map.put(name, (SingleTon3)Class.forName(name).newInstance()); 29 30 return map.get(name); 31 } 32 }
四、双重检查锁
这里我们用懒汉式来介绍双重检查锁,
1 class SingleTon2 { 2 3 private SingleTon2() {} 4 5 static SingleTon2 st2 = null; 6 7 public static SingleTon2 getInstaTon2 () { 8 if (st2 == null) 9 st2 = new SingleTon2(); 10 11 return st2; 12 } 13 }
在getInstaTon2 ()方法中有一步判断是否为空的操作,比如现在有2个线程A与B,同时到达判断这一步,在这一瞬间实例并没有创建,所以他们都能通过判断去创建2个实例,这就与单例的唯一实例相违背了;
但是我们通过双重检查锁就可以规避这种极端的情况:
1 class SingleTon2 { 2 3 private SingleTon2() {} 4 5 static SingleTon2 st2 = null; 6 7 public static SingleTon2 getInstaTon2 () { 8 if (st2 == null) { 9 synchronized (st2) { 10 if (st2 == null) 11 st2 = new SingleTon2(); 12 } 13 } 14 15 return st2; 16 } 17 }