1.什么是单例模式?
《Head First 设计模式》中给出如下定义:确保一个类只有一个实例,并提供一个全局访问点。
关键词:唯一实例对象。
2.单例模式的实现方式:
2.1 懒汉式
对于实例做懒加载处理,即在客户第一次使用时再做创建,所以第一次获取实例的效率会稍微低一些。
1 /** 2 * 懒汉式 3 * @author Lsj 4 */ 5 public class LazySingleton { 6 7 private static LazySingleton instance; 8 9 /** 10 * 获取单一实例对象—非同步方法 11 * @return 12 */ 13 public static LazySingleton getInstance(){ 14 if(instance == null){ 15 try { 16 TimeUnit.NANOSECONDS.sleep(1);//为了使模拟效果更直观,这里延时1ms,具体看时序图 17 } catch (InterruptedException e) { 18 // TODO Auto-generated catch block 19 e.printStackTrace(); 20 } 21 instance = new LazySingleton(); 22 } 23 return instance; 24 } 25 26 }
这种创建方式可以延迟加载、但是在多线程环境下获取到的实例可能并非唯一的,具体见如下验证:
1 import java.util.concurrent.CountDownLatch; 2 import java.util.concurrent.TimeUnit; 3 4 /** 5 * 懒汉式 6 * @author Lsj 7 */ 8 public class LazySingleton { 9 10 private static LazySingleton instance; 11 12 /** 13 * 获取单一实例对象—非同步方法 14 * @return 15 */ 16 public static LazySingleton getInstance(){ 17 if(instance == null){ 18 try { 19 TimeUnit.NANOSECONDS.sleep(1);//为了使模拟效果更直观,这里延时1ms,具体看时序图 20 } catch (InterruptedException e) { 21 // TODO Auto-generated catch block 22 e.printStackTrace(); 23 } 24 instance = new LazySingleton(); 25 } 26 return instance; 27 } 28 29 /** 30 * 增加同步锁 31 * 避免多线程环境下并发产生多个实例可能的同时,会带来性能上的损耗。 32 * 事实上只有第一次创建时需要这么做,但后续依然通过加锁获取单例对象就有点因小失大了。 33 * @return 34 */ 35 public synchronized static LazySingleton getInstanceSyn(){ 36 if(instance == null){ 37 try { 38 TimeUnit.MILLISECONDS.sleep(1);//为了使模拟效果更直观,这里延时1ms,具体看时序图 39 } catch (InterruptedException e) { 40 // TODO Auto-generated catch block 41 e.printStackTrace(); 42 } 43 instance = new LazySingleton(); 44 } 45 return instance; 46 } 47 48 public static void main(String[] args) throws InterruptedException { 49 //模拟下多线程环境下实例可能不唯一的情况 50 CountDownLatch startSignal = new CountDownLatch(1); 51 for(int i=0;i<2;i++){//模拟2个线程 52 Thread t = new Thread(new MyThread(startSignal)); 53 t.setName("thread " + i); 54 t.start(); 55 } 56 Thread.sleep(1000); 57 startSignal.countDown(); 58 } 59 60 } 61 62 class MyThread implements Runnable { 63 64 private final CountDownLatch startSignal; 65 66 public MyThread(CountDownLatch startSignal){ 67 this.startSignal = startSignal; 68 } 69 70 public void run() { 71 try { 72 System.out.println("current thread : " + Thread.currentThread().getName() + " is waiting."); 73 startSignal.await(); 74 } catch (InterruptedException e) { 75 // TODO Auto-generated catch block 76 e.printStackTrace(); 77 } 78 LazySingleton l = LazySingleton.getInstance(); 79 System.out.println(l); 80 } 81 82 }