单例模式属于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 singletons= new HashSet(); 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; } }
推荐大家在多线程开发中使用双重检查单例和静态内部类单例,集成了懒汉和饿汉的优点。
如何只是单线程没有线程同步情况的话按照情况选择懒汉和饿汉式。
学习过程中,如有不对,请指出。