- 定义
-
保证一个类仅有一个实例,并提供一个访问它的全局访问点——《设计模式》
* 单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例
* 单例模式是通过private
限定,避免了类在外部被实例化
* 通过Java反射机制是能够实例化构造方法为private的类的,这基本会所有的java单例实现失效,故使用单例默认是指不使用反射的情况
-
- 特点
- 单例类只能有一个实例或给定数目的单例
- 单例类必须自己创建自己的唯一实例
- 单例类必须给所有其他对象提供这个实例
- 分类
- 常见的分为饿汉模式和懒汉模式
- 还有其他的如登记式单例模式,枚举,双重校验锁,内部静态类等
- 有个牛人一下总结了七种单例模式:具体参考单例模式的七种写法
- 单例模式的扩展:
有上限的多例模式
- 优点
- 避免对资源的多重占用,避免对同一个资源文件的同时操作,造成文件状态不一致
- 减少内存开支,尤其是需要频繁创建和使用的类
- 对象创建需要较多资源时,使用单例模式,可以减少系统开销
- 设置全局访问点,优化和共享资源访问
- 缺点
- 没有接口,不能扩展
- 单例类的职责过重,在一定程度上违反了"单一职责原则"
- 单例模式的使用场景
- 具有资源管理器功能的应用:线程池,缓存,日志,对话,打印机等
- 整个项目中需要一个共享访问带点或共享数据
- 创建一个对象需要消耗的资源过多,如访问IO和数据库等资源
- 需要定义大量的静态常量和静态方法的环境,可以采用单例模式(或直接采用static)
- 多个对象可能会造成影响等
- 对系统资源的使用有限定
- 示例
- 饿汉模式
//一般情况下,推荐使用这种单例,在类加载时,就开始实例化,线程安全,不能延迟加载 public class Singleton{ private static final Singleton instance = new Singleton(); private Singleton(){} private static Singleton getInstance(){ return instance; } }
- "变种"饿汉
// public class Singleton{ private static Singleton instance = null; static{ instance = new Singleton(); } private Singleton(){} public static Singleton getInstance(){ return this.instance; } }
- 懒汉模式
public class Singleton{ private static Singleton instance = null; private Singeton(){} //线程安全的,并可以很好地延迟加载,但效率很低 //private static synchronized Singleton getInstance() //线程不安全,可以延迟加载,但多线程下工作不能正常工作,不建议使用 public static Singleton getInstance(){ if(instance==null){ instance = new Singleton(); } return instance; } }
- 静态内部类
//利用了classloder的机制来保证初始化instance时只有一个线程 //Singleton类加载时,SingleHolder并没有加载,在调用getInstance()才开始加载,实现延迟加载的目的 public class Singleton{ private static class SingleHolder{ public static final Singleton INSTANCE = new Singleton(); } private Singleton(){} private static final getInstance(){ return SingleHolder.INSTANCE; } }
- 枚举
//能避免多线程同步问题,还能防止反序列化重新创建新的对象 public enum Singleton{ INSTANCE; public void whateverMethod(){ } }
- 双重校验锁
// 线程安全,运行效率低,不建议大量采用 public class Singleton { //volatile 关键字:被volatile修饰的变量的值,不会被本地线程缓存,所有对该变量的读写都是直接操作内存,从而确保多个线程能正确处理该变量 private volatile static Singleton singleton=null; private Singleton(){} public static Singleton getSingleton(){ if(singleton==null){ //同步锁 synchronized(Singleton.class){ if(singleton==null){ singleton = new Singleton(); } } } } return singleton; }
- 登记式单例模式
//登记式实际对一组单例模式进行的维护,即对多个单例类进行维护,主要是在数量上的扩展 //通过map我们把单例存进去,这样在调用时,先判断该单例是否已经创建,是的话直接返回,不是的话创建一个登记到map中,在返回 // 对于数量分为固定数量和不固定数量 public class Singleton { private static Map<String,Singleton> map = new HashMap<String,Singleton>(); static{ Singleton instance = new Singleton(); map.put(single.getClass.getName(),instance); } private Singleton(){} public static Singleton getInstance(String name){ if(name = null){ name = Singleton.class.getName(); } if(map.get(name)==null){ try{ map.put(name,(Singleton)Class.forName(name).newInstance()); }catch(InstantitionException e){ e.printStackTrace(); }catch(IllegallAccessException e){ e.printStackTrace(); }catch (ClassNotFoundException e) {
27 e.printStackTrace();
28 }
}
return map.get(name);
}
}
```
* 总结
* 其中饿汉
和静态内部
这两种方式比较易用,简单易懂,且线程安全
* 在没有明确要求延迟加载的情况下,推荐使用饿汉模式
* 明确要求延迟加载,则推荐使用静态内部类
* 懒汉模式
不建议使用,即使使用了synchronized
,保证了线程安全,但加载速度也会很慢
网友评论