以下是三种单例模式的代码实现,前两者用的比较多 (言外之意 最后一种可以忽略)
1 package com.signle; 2 3 import java.util.HashMap; 4 import java.util.Map; 5 6 /** 7 * 8 * @title 单例模式 9 * @Copyright Copyright (c)2016年3月9日 10 * @Company CTC 11 * @version 1.0 12 * @author ejokovic 13 * @time 上午11:21:53 14 * @package 15 * com.signle 16 * 17 */ 18 public class TestSignle { 19 public static void main(String[] args) { 20 Singleton s = Singleton.getInstance(); 21 Singleton s1 = Singleton.getInstance(); 22 //System.out.println(s==s1); 23 SigletionC singleC = SigletionC.getInstance(null); 24 System.out.println(singleC.about()); 25 } 26 27 } 28 29 /** 30 * 懒汉式 31 * 是线程不安全的并发环境下很可能出现多个Singleton实例,要实现线程安全 32 * @title 33 * @Copyright Copyright (c)2016年3月9日 34 * @Company CTC 35 * @version 1.0 36 * @author ejokovic 37 * @time 上午11:23:44 38 * @package 39 * com.signle 40 * 41 */ 42 class Singleton{ 43 private Singleton(){} 44 private static Singleton Singleton; 45 public static Singleton getInstance(){ 46 if(Singleton==null){ 47 Singleton = new Singleton(); 48 } 49 return Singleton; 50 } 51 } 52 53 /** 54 * 解决懒汉式单例线程安全(一) 55 * 在getInstance方法上加同步锁 56 * @title 57 * @Copyright Copyright (c)2016年3月9日 58 * @Company CTC 59 * @version 1.0 60 * @author ejokovic 61 * @time 上午11:31:45 62 * @package 63 * com.signle 64 * 65 */ 66 class Singleton2{ 67 private Singleton2(){} 68 private static Singleton2 Singleton; 69 public static synchronized Singleton2 getInstance(){ 70 if(Singleton==null){ 71 Singleton = new Singleton2(); 72 } 73 return Singleton; 74 } 75 } 76 77 /** 78 * 解决懒汉式单例线程安全(二) 79 * 双重检查锁定 80 * @title 81 * @Copyright Copyright (c)2016年3月9日 82 * @Company CTC 83 * @version 1.0 84 * @author ejokovic 85 * @time 上午11:34:42 86 * @package 87 * com.signle 88 * 89 */ 90 class Singleton3{ 91 private Singleton3(){} 92 private static Singleton3 Singleton; 93 public static synchronized Singleton3 getInstance(){ 94 if(Singleton==null){ 95 synchronized (Singleton3.class){ 96 if(Singleton==null){ 97 Singleton = new Singleton3(); 98 } 99 } 100 } 101 return Singleton; 102 } 103 } 104 105 /** 106 * 解决懒汉式单例线程安全(二) 107 * 静态内部类 108 * 这种比上面1、2都好一些,既实现了线程安全,又避免了同步带来的性能影响。 109 * @title 110 * @Copyright Copyright (c)2016年3月9日 111 * @Company CTC 112 * @version 1.0 113 * @author ejokovic 114 * @time 下午12:06:54 115 * @package 116 * com.signle 117 * 118 */ 119 class Singleton4{ 120 private static class LazyHolder{ 121 private static final Singleton4 INSTANCE = new Singleton4(); 122 } 123 private Singleton4(){} 124 public static synchronized Singleton4 getInstance(){ 125 return LazyHolder.INSTANCE; 126 } 127 } 128 129 /** 130 * 饿汉式单例类.在类初始化时,已经自行实例化 131 * @title 132 * @Copyright Copyright (c)2016年3月9日 133 * @Company CTC 134 * @version 1.0 135 * @author ejokovic 136 * @time 下午12:12:23 137 * @package 138 * com.signle 139 * 140 */ 141 class SigletionA{ 142 private SigletionA(){} 143 private static final SigletionA sigletionA = new SigletionA(); 144 public static SigletionA getInstance(){ 145 return sigletionA; 146 } 147 } 148 149 //登记式单例 150 class SigletionC{ 151 private static Mapmap = new HashMap (); 152 static{ 153 SigletionC single = new SigletionC(); 154 map.put(single.getClass().getName(), single); 155 } 156 //保护的默认构造子 157 protected SigletionC(){} 158 //静态工厂方法,返还此类惟一的实例 159 public static SigletionC getInstance(String name) { 160 if(name == null) { 161 name = SigletionC.class.getName(); 162 System.out.println("name == null"+"--->name="+name); 163 } 164 if(map.get(name) == null) { 165 try { 166 map.put(name, (SigletionC) Class.forName(name).newInstance()); 167 } catch (InstantiationException e) { 168 e.printStackTrace(); 169 } catch (IllegalAccessException e) { 170 e.printStackTrace(); 171 } catch (ClassNotFoundException e) { 172 e.printStackTrace(); 173 } 174 } 175 return map.get(name); 176 } 177 //一个示意性的商业方法 178 public String about() { 179 return "Hello, I am RegSingleton."; 180 } 181 }
在了解饿汉与懒汉的区别前 先了解下什么是线程安全:
假如你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时执行这段代码,如果每次执行的结果和单线程中执行的结果一致 且 其它变量的值也和预期的一致 这就是线程安全
换句话说:
一个类或者程序提供的接口对于线程来说是原子操作,或者多个线程之间切换不会导致该接口的执行结果存在差异性 也就是说我们不用考虑同步的问题 那就是线程安全的
饿汉式和懒汉式区别:
从名字上来说,饿汉和懒汉:
饿汉是当类加载时就把单例初始化完成,保证getInstance的时候,单例是已经存在的了
而懒汉比较懒,只有当调用getInstance的时候,才会去初始化这个单例;
从线程是否安全角度讲:
饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题
懒汉式本身是非线程安全的,为了实现线程安全有几种写法,分别是上面的1、2、3,这三种实现在资源加载和性能方面有些区别;
从资源加载和性能的角度讲:
饿汉式在类加载的同时就实例化一个静态对象出来,不管之后是否用到这个静态对象,都会占据一定的内存,但是相应的 在第一次调用时的速度也会更快,因为资源已经初始化好了;
至于以上1,2,3 这三种实现又有些区别
第一种,在方法调用上加了同步,虽然线程安全了,但每次都要同步,会影响性能,毕竟大部分的情况下是不需要同步的
第二种,在getInstance方法中做了两次null检查,确保了只有第一次调用该静态对象的时候才会同步,这样也是线程安全的,同时避免了每次调用时的同步损耗;
第三种,利用了classloader机制,保证初始化instance时只有一个线程 所以是线程安全的 同时没有性能损耗