单例模式的特点:
1、单例模式只能有一个实例
2、单利类必须自己创建自己的唯一实例
3、单例类必须给所有的其他对象提供这一实例
概述:保证一个类,只有一个实例存在,同时提供对该实例加以访问的全局访问方法。
单例模式的运用:线程池,缓存,日志对象,对话框,打印机,显卡驱动常常被设计为单例模式。主要就是避免不一致状态。
单例模式的实现方式:
懒汉式
饿汉式
双重检查
一、懒汉式
1 package cn.zpoor.singletonBlog; 2 //懒汉式单例模式,在第一次调用的时候实例化自己 3 public class Singleton { 4 private static Singleton singleton = null; 5 private Singleton() {} 6 7 //实现全局方法(静态工厂方法) 8 public static Singleton getSingleton() { 9 if (singleton == null) { 10 singleton = new Singleton(); 11 } 12 return singleton; 13 } 14 }
解释:
私有化的构造方法,避免在外部类实例化,Singleton的唯一实例只能通过getSingleton()方法访问。
但是懒汉式单例没有考虑线程的安全,多线程环境下可能会出现多个Singleton实例,实现线程安全有多种方法。举个栗子
1、在getSingleto()方法加上同步锁
1 public static synchronized Singleton getSingleton() { 2 if (singleton == null) { 3 singleton = new Singleton(); 4 } 5 return singleton; 6 }
2、双重检查
1 public static Singleton getSingleton() { 2 if (singleton == null) { 3 synchronized (Singleton.class) { 4 if (singleton == null) { 5 singleton = new Singleton(); 6 } 7 } 8 } 9 return singleton; 10 }
3、静态内部类(实现了线程安全,又避免同步带来的性能影响)
1 public class Singleton { 2 public static class Lazy { 3 private static final Singleton INSTANCE = new Singleton(); 4 } 5 6 private Singleton() { 7 8 } 9 10 public static final Singleton getSingleton() { 11 return Lazy.INSTANCE; 12 } 13 }
二、饿汉式
1 package cn.zpoor.singletonBlog; 2 //饿汉式单例,在类初始化时,已经自行初始化 3 public class Singleton { 4 private static final Singleton SINGLETON = new Singleton(); 5 6 private Singleton() { 7 8 } 9 10 //静态全局方法 11 public static Singleton getSingleton () { 12 return SINGLETON; 13 } 14 }
在类创建的时候就已经创建好一个静态的对象提供给系统使用,不在改变,所以线程是安全的。
懒汉和饿汉的区别:
字面上一个懒一个饿。
饿汉:类一旦初始化,单例初始化完成,调用getSingleton的时候,单例已经存在
懒汉:只有在调用getSingleton的时候,才会去初始化这个单例
从线程安全上面:
饿汉式天生就是安全的,可以直接用于多线程,不会出现问题
懒汉式本身是非线程安全的,实现线程安全一般是加同步锁、双重检查,静态内部类
从资源加载和性能上面:
饿汉式在类创建的时候就会实例化一个静态的对象,不管以后用不用,都会占据内存,相应的在第一次调用的速度会很快,已经初始化完成了。
懒汉式会延迟加载,第一次使用该单例的时候才会实例化该对象出来,第一次调用会初始化,速度上比较慢一点。
1、同步锁
在方法上调用上加上同步锁,线程安全了,但是每次都要同步,会影响性能
2、双重检查
在getSingleton中做了两次null检查,确保了只有第一次调用该单例的时候才会做同步,这样也是线程安全的,同时避免了每次同步的性能损耗。
3、静态内部类
利用了classloader的机制来保证初始化INSTANCE时只有一个线程,所以线程也是安全的,同时没有性能的损耗。(推荐)
什么是线程安全:
代码在所在的进程中有多个线程在同时运行,这些线程可能会同时运行这些代码。如果每次运行的结果和单线程运行的结果一致,其他变量的值也是和预期一致,这样就是线程安全的。
一个类或者程序提供的接口对于线程来说是源自操作,或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是不用考虑同步问题,这也是线程安全的
单例模式的应用:
饿汉式,使用双重检查
1 package cn.zpoor.singletonBlog; 2 public class Singleton { 3 private String name; 4 private static volatile Singleton instance = null; 5 6 public String getName() { 7 return name; 8 } 9 10 public void setName(String name) { 11 this.name = name; 12 } 13 14 private Singleton() {} 15 16 public static Singleton getInstance() { 17 if (instance == null) { 18 synchronized (Singleton.class) { 19 if (instance == null) { 20 instance = new Singleton(); 21 } 22 } 23 } 24 return instance; 25 } 26 27 public void info() { 28 System.out.println("name is" + name); 29 } 30 }
测试代码:
1 package cn.zpoor.singletonBlog; 2 3 public class TestSingleton { 4 public static void main(String[] args) { 5 Singleton s1 = Singleton.getInstance(); 6 Singleton s2 = Singleton.getInstance(); 7 8 s1.setName("WY"); 9 s2.setName("Zpoor"); 10 11 s1.info(); 12 s2.info(); 13 14 if (s1 == s2) { 15 System.out.println("这是单例模式"); 16 } else { 17 System.out.println("这是多例模式"); 18 } 19 } 20 } 21 22 23 /*结果: 24 25 name isZpoor 26 name isZpoor 27 这是单例模式 28 */
总结:
一个类只有一个实例,还要提供全部访问的方法,注意懒汉饿汉的区别,线程安全等等。