一 什么是单例模式
单例模式指的是使用单例模式创建的类实例,在程序的生命周期内,只有一个唯一的实例对象被其他类调用,即其他类调用的此类的实例对象都是相同的一个。
二 单例实现 分析
单例的实现有多种方法,我们结合代码来看
1 public class Singleton1 { 2 3 private static Singleton1 instance; 4 5 public Singleton1(){ 6 } 7 8 public static Singleton1 getInstance(){ 9 if(instance == null){10 System.out.println("创建实例对象");11 instance = new Singleton1();12 }13 return instance;14 }15 16 }
这是基本的一种单例实现方式,当被需要的时候才会创建实例对象,所以称为懒汉式(lazy loading)实现,没有调用时不会创建实例,不会占用内存空间。但是这种方式实现有一个问题,就是在多线程情况下,无法保证线程安全。比如说我们有两个线程A和B都调用了getInstance方法,初始时instance为空,A线程进入了if代码语句块中的第11行(没有执行),此时B线程也就进入到了if代码语句块中的第10行。此时线程A和B会创建出2个不同的实例对象,就不能保证线程正常工作了,并且也不满足唯一实例存在的设计原则。对于这种多线程安全问题,我们可以通过加锁方式处理。加锁一般有2中方式,分别是类锁(在类的实例class上加锁)和对象锁(在实例对象方法上加锁),下边我们用加锁的方式实现
1 public class Singleton2 { 2 3 private static Singleton2 instance; 4 5 public Singleton2(){ 6 } 7 8 public static synchronized Singleton2 getInstance(){ 9 if(instance == null){10 instance = new Singleton2();11 }12 return instance;13 }14 15 }
1 public class Singleton3 { 2 3 private static Singleton3 instance; 4 5 public Singleton3(){ 6 } 7 8 public static Singleton3 getInstance(){ 9 if(instance == null){10 synchronized(Singleton3.class){11 if(instance == null){12 instance = new Singleton3();13 }14 }15 }16 return instance;17 }18 19 }
这两种方式,我们分别通过加锁和双重锁校验的方式确保并发时只能有一个线程拿到资源去创建实例,其他的只能等着用创建好的实例。就能确保只会有一个实例了。但是加了锁之后,程序的执行效率会受到影响
1 public class Singleton4 { 2 3 private static Singleton4 instance = new Singleton4(); 4 5 public Singleton4() { 6 } 7 8 public static Singleton4 getInstance(){ 9 return instance;10 }11 12 }
这种方法是在类加载的时候,直接就创建了一个实例对象,当有需要的时候,直接将这个实例返回以供使用。这种方式下不管是否多线程都只有一个实例,所以保证了线程安全且符合只有一个实例对象存在的设计原则。缺点就是如果我在程序的整个生命周期内都没有用到它,那它就属于多余的,造成了内存资源的无效的使用(内存资源被占用但没有使用)。
1 public class Singleton5 { 2 3 private static class SingletonHolder { 4 private static Singleton5 instance = new Singleton5(); 5 } 6 7 public Singleton5() { 8 } 9 10 public static Singleton5 getInstance(){11 return SingletonHolder.instance;12 }13 14 }
这种方式采用静态内部类实现,在Singleton5被加载时并不会直接去创建实例,只有当方法getInstance被调用后才会去调用SingletonHolder 创建实例。