本文属于原创内容,如需转载请标明来源。
单例模式属于java设计模式的一种,最常见实现方式有以下几种 懒汉、饿汉、双重检查单例、静态内部类单例。
单例模式的特点:
1:单例类只能有一个实例
2:单例类的唯一实例化必须由自己完成
3:单例类给其他对象提供唯一实例
如何保证第一个和第三个特点呢->2个实例化的对象相等说明是同一实例化对象
1 public class SingletonTest { 2 3 public static void main(String[] args) { 4 Singleton singleton1=Singleton.getInstance(); 5 Singleton singleton2=Singleton.getInstance(); 6 /* 7 * 利用Set的特性检验2个对象是同一个实例 8 * 输出1代表这两个变量代表的同一个实例对象 9 * 10 */ 11 Set<Singleton> set=new HashSet<Singleton>(); 12 set.add(singleton1); 13 set.add(singleton2); 14 System.out.println("set长度"+set.size()); 15 //set长度1 16 } 17 }
如何理解第二个特点:单例类是的实例化必须由自己完成->私有化构造器
private Singleton() { }
1 package com.innerclass; 2 3 public class SingletonTest { 4 5 public static void main(String[] args) { 6 //我们在同包中创建一个其他类 并尝试创建Singleton实例 得的一个错误 7 //The constructor Singleton() is not visible 8 //构造方法Singleton() 是不可见的 也就是说我们无法创建Singleton的实例对象 9 Singleton singleton=new Singleton(); 10 11 } 12 }
- 饿汉式的实现(饿汉式也就是不管你用不用我都把实例化创建好放在这里,你需要用的时候就拿去用)
优点:始终只有一个singleton实例对象 所以线程安全
在类加载的同时已经创建好一个静态对象,调用时反应速度快
缺点:jvm加载类的时候一定会实例化,如果一直没调用getInstance()方法,会造成资源的浪费。
1 public class Singleton { 2 private Singleton() { 3 } 4 private static Singleton singleton=new Singleton(); 5 public static Singleton getInstance() { 6 return singleton; 7 } 8 }
- 线程安全的懒汉式(何为懒汉也就是按需加载 只有在使用的时候才对单例类去初始化)
优点:按需加载,不会造成资源的浪费
缺点:无synchronized关键字的单例类会造成线程的不同步
1 private Singleton() { 2 3 } 4 public static Singleton singleton=null; 5 public synchronized Singleton getInstance(){ 6 if(singleton==null) { 7 return new Singleton(); 8 } 9 return singleton; 10 }
此处说一下为什么要给getInstance()方法加锁(实际意义上是给Singleton.class类类型加锁,有兴趣可以去了解一下)
假设上面的代码中没有 synchronized 关键字
public class Singleton { private Singleton() { } private static Singleton singleton=null; public static Singleton getInstance(){ if(singleton==null) { try {
//假设线程阻塞情况 Thread.sleep(100); return new Singleton(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return singleton; } public static void main(String[] args) { Set<Singleton> singletons= new HashSet<Singleton>(); for (int i = 0; i < 10; i++) { singletons.add(Singleton.getInstance()); } System.out.println(singletons.size());
//10
//说明多线程下懒汉式可能会创建多个实例对象
} }
这种情况下,线程安全可以保证,但是效率问题受到人的诟病了。因为线程第一次实例化类之后,往后每次获取实例化对象仍然需要去获取单例类的锁和释放锁。增加了性能的损耗。于是有了以下2中进阶方式的单例模式
- 双重检查单例(不同于上一个懒汉式实现方式 只有当对象未实例化的时候才选择去加锁创建唯一实例,若是对象已初始化直接返回已初始化对象,提高了效率)
1 public class Singleton { 2 /** 3 * 双重检查单例 4 */ 5 private Singleton() { 6 7 } 8 private static volatile Singleton singleton; 9 public static Singleton getInstance() { 10 if(singleton!=null) { 11 synchronized (Singleton.class) { 12 if(singleton!=null) { 13 singleton=new Singleton(); 14 } 15 } 16 } 17 return singleton; 18 } 19 }
volatile关键字 在这里不做叙述,有兴趣的可以直接去百度它的作用
- 静态内部类实现单例(利用原理是内部类的对外不可见性)
public class Singleton { private Singleton() { } private static class SingletonHandler{ private static Singleton singleton=new Singleton(); } public Singleton getInstance() { return SingletonHandler.singleton; } }
推荐大家在多线程开发中使用双重检查单例和静态内部类单例,集成了懒汉和饿汉的优点。
如何只是单线程没有线程同步情况的话按照情况选择懒汉和饿汉式。
学习过程中,如有不对,请指出。