设计模式Design Pattern(2)--单例模式
单例顾名思义就是一个实例。类只有唯一一个实例,并提供给全局使用。解决了全局使用的类频繁地创建与销毁带了的消耗。
单例模式常用简单,但细究却又不简单,且往下看。
单例模式又可以分为
(1)懒汉式:需要使用实例时,才创建实例
(2)饿汉式:类加载时,就创建静态实例。
上代码
1、饿汉式,线程安全
1 /** 2 * 饿汉式--线程安全 3 * 优点:没有加锁,执行效率会提高。 4 * 缺点:类加载时就初始化,浪费内存。 5 */ 6 public class UserHunger_Safe { 7 /** 8 * 对象实例 9 */ 10 private static UserHunger_Safe instance = new UserHunger_Safe(); 11 12 /** 13 * 私有构造函数 14 */ 15 private UserHunger_Safe() { 16 } 17 18 /** 19 * 对外提供公共获取实例方法(线程安全) 20 * 21 * @return 22 */ 23 public static UserHunger_Safe getInstance() { 24 return instance; 25 } 26 }
2、懒汉式,线程不安全
1 /** 2 * 懒汉式单例模式 3 * 线程不安全 4 */ 5 public class UserLazy_Unsafe { 6 7 /** 8 * 私有构造方法 9 */ 10 private UserLazy_Unsafe() { 11 } 12 13 /** 14 * 对象实例 15 */ 16 private static UserLazy_Unsafe instance; 17 18 /** 19 * 对外提供公共获取实例方法(线程不安全) 20 * 21 * @return 22 */ 23 public static UserLazy_Unsafe getInstance() { 24 if (instance == null) { 25 instance = new UserLazy_Unsafe(); 26 } 27 return instance; 28 } 29 }
3、懒汉式--方法加锁synchronized,线程安全
1 /** 2 * 懒汉式单例模式 3 * 优点:第一次调用才初始化,避免内存浪费。 4 * 缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。 5 */ 6 public class UserLazy_Safe { 7 /** 8 * 私有构造方法 9 */ 10 private UserLazy_Safe() { 11 } 12 13 /** 14 * 对象实例 15 */ 16 private static UserLazy_Safe instance; 17 18 /** 19 * 对外提供公共获取实例方法(线程安全) 20 * 21 * @return 22 */ 23 public static synchronized UserLazy_Safe getInstance() { 24 if (instance == null) { 25 instance = new UserLazy_Safe(); 26 } 27 return instance; 28 } 29 }
方法加锁,则每次调用都需要锁定,严重影响性能,这不是想要的。所以优化下得出第4中方案
4、双重校验锁(DCL,即 double-checked locking)
1 /** 2 * 饿汉式+双重校验锁 3 */ 4 public class UserDCL_Safe { 5 /** 6 * 对象实例 7 */ 8 private static UserDCL_Safe instance; 9 10 /** 11 * 私有构造函数 12 */ 13 private UserDCL_Safe() { 14 } 15 16 /** 17 * 对外提供公共获取实例方法(线程安全) 18 * 19 * @return 20 */ 21 public static synchronized UserDCL_Safe getInstance() { 22 if (instance == null) { 23 //双重校验锁 24 synchronized (UserDCL_Safe.class) { 25 if (instance == null) { 26 instance = new UserDCL_Safe(); 27 } 28 } 29 } 30 return instance; 31 } 32 }
如此一来,即便是多线程也能保证安全。如果只考虑静态域还可以使用内部静态类
5、静态内部类
1 /** 2 * 登记式/静态内部类 3 * 线程安全 4 * 这种方式能达到双检锁方式一样的功效,但实现更简单。 5 * 这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。 6 */ 7 public class UserStaticInnerClass_Safe { 8 private static class SingletonHolder{ 9 private static final UserStaticInnerClass_Safe INSTANCE=new UserStaticInnerClass_Safe();//实例 10 } 11 12 //私有构造函数 13 private UserStaticInnerClass_Safe(){} 14 public static final UserStaticInnerClass_Safe getInstance(){ 15 return SingletonHolder.INSTANCE; 16 } 17 }
至此,单例模式是不是绝对安全了?答案当然不是,在反射攻击下,外部依旧能做到非单例,不信,读者可以通过反射方式获得以上1,3,4,5例子的实例,就会发现依旧可以得到不同的实例。那有什么好的解决方案呢?
这个Effective Java 作者 Josh Bloch 大神给出了解决方案--使用枚举
6、枚举,最佳实现方式
1 /** 2 * 枚举方式实现单例模式 3 */ 4 public enum UserEnum { 5 INSTANCE; 6 7 /** 8 * 姓名 9 */ 10 private String name; 11 12 /** 13 * 年龄 14 */ 15 private int age; 16 17 /** 18 * 手机号码 19 */ 20 private String telephone; 21 22 public String getName() { 23 return name; 24 } 25 26 public void setName(String name) { 27 this.name = name; 28 } 29 30 public int getAge() { 31 return age; 32 } 33 34 public void setAge(int age) { 35 this.age = age; 36 } 37 38 public String getTelephone() { 39 return telephone; 40 } 41 42 public void setTelephone(String telephone) { 43 this.telephone = telephone; 44 } 45 46 @Override 47 public String toString() { 48 return "SingletonPattern_EnumType{" + 49 "name='" + name + '\'' + 50 ", age=" + age + 51 ", telephone='" + telephone + '\'' + 52 '}'; 53 } 54 }
如此一来既保证了线程安全,也保证了反序列化安全
测试
1 import java.lang.reflect.Constructor; 2 3 public class SigletonPattern { 4 public static void main(String[] args) throws Exception { 5 //懒汉式单例--线程不安全 6 UserLazy_Unsafe ul1 = UserLazy_Unsafe.getInstance(); 7 UserLazy_Unsafe ul2 = UserLazy_Unsafe.getInstance(); 8 System.out.println(ul1 + "\n" + ul2); 9 System.out.println("===========懒汉式单例--线程不安全 End=========="); 10 11 //懒汉式单例--线程安全.获取实例方法添加synchronized,加锁会影响效率。 12 UserLazy_Safe uls1 = UserLazy_Safe.getInstance(); 13 UserLazy_Safe uls2 = UserLazy_Safe.getInstance(); 14 System.out.println(uls1 + "\n" + uls2); 15 System.out.println("===========懒汉式单例--线程安全 End=========="); 16 17 //懒汉式单例--登记式/静态内部类--线程安全。 18 UserStaticInnerClass_Safe usic = UserStaticInnerClass_Safe.getInstance(); 19 UserStaticInnerClass_Safe usic2 = UserStaticInnerClass_Safe.getInstance(); 20 System.out.println(usic + "\n" + usic2); 21 System.out.println("===========懒汉式单例--登记式/静态内部类--线程安全 End=========="); 22 23 UserEnum user = UserEnum.INSTANCE; 24 //UserEnum user = new UserEnum(); 25 user.setName("科技无国界"); 26 user.setAge(0); 27 user.setTelephone("16895965423"); 28 29 UserEnum levon = UserEnum.INSTANCE; 30 //UserEnum levon = new UserEnum(); 31 levon.setName("联想--科技无国界"); 32 levon.setAge(10); 33 levon.setTelephone("26895965423"); 34 35 System.out.println(user); 36 System.out.println(levon); 37 38 //通过反射获得对象 39 Constructor<UserEnum> constructor = UserEnum.class.getDeclaredConstructor(); 40 constructor.setAccessible(true); 41 UserEnum sp = constructor.newInstance(); 42 System.out.println(sp); 43 } 44 }
1 UserLazy_Unsafe@1b6d3586 2 UserLazy_Unsafe@1b6d3586 3 ===========懒汉式单例--线程不安全 End========== 4 UserLazy_Safe@4554617c 5 UserLazy_Safe@4554617c 6 ===========懒汉式单例--线程安全 End========== 7 UserStaticInnerClass_Safe@74a14482 8 UserStaticInnerClass_Safe@74a14482 9 ===========懒汉式单例--登记式/静态内部类--线程安全 End========== 10 SingletonPattern_EnumType{name='联想--科技无国界', age=10, telephone='26895965423'} 11 SingletonPattern_EnumType{name='联想--科技无国界', age=10, telephone='26895965423'} 12 Exception in thread "main" java.lang.NoSuchMethodException: UserEnum.<init>() 13 at java.lang.Class.getConstructor0(Class.java:3082) 14 at java.lang.Class.getDeclaredConstructor(Class.java:2178) 15 at SigletonPattern.main(SigletonPattern.java:39)
如果该文章对你有所帮助,请点个赞支持下,谢谢!