几种单例模式解析
1)单例模式的定义:
在整个应用中,保证一个类只有一个实例,它提供了一个可以访问到它自己的全局访问点(静态方法)。
2)单例模式的优缺点:
优点:
1、提供了对唯一实例的受控访问;
2、Java中频繁创建和销毁类对象都会占用一部分系统资源,使用单例模式可以提高性能;
3、允许可变数量的实例;
缺点:
1、单例模式中,没有抽象层,所以对于单例类的扩展并不方便;
2、单例类的职责过重,在一定程度上违背了“单一职责原则”;
3、滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失;
3)简单介绍几种单例,单例模式中有区分了懒汉式和饿汉式,懒汉式主要是用时间来换空间,饿汉式则是用空间来换时间。饿汉式是线程安全的,懒汉式是非线程安全的,如果要实现懒汉式的非线程安全,则可以再访问点添加synchronized关键字声明即可。在其他的一些项目中还使用了双重检测枷锁机制。
1、懒汉单例,存在线程不安全问题,如启动多线程,同时调用入口方法获取实例时不能正常工作:
//懒汉单例,线程不安全,当启动多线程,同时获取多个实例时会报错 private static PostContentForResponse instance = null; private PostContentForResponse() {} public static PostContentForResponse getInstance() { if(instance == null) { instance = new PostContentForResponse(); } return instance; }
懒汉模式也提供一种解决同步调用时不能正常工作的方法,使用synchronized声明访问点,但是工作效率低
private static PostContentForResponse instance = null; private PostContentForResponse() {} public static synchronized PostContentForResponse getInstance() { if(instance == null) { instance = new PostContentForResponse(); } return instance; }
2、饿汉单例,这种方式基于classloder机制避免了多线程的同步问题,但是instance在类装载时就实例化,虽然导致类装载的原因有很多种,
在单例模式中大多数都是调用getInstance方法,但是也不能确定有其他的方法(或者其他静态方法)导致类装载,这是初始化的instance
显然没有达到lazy loading的效果。
private static PostContentForResponse instance = new PostContentForResonse(); private PostContentForResponse() {} public static PostContentForResponse getInstance() { return instance; }
3、静态内部类,跟饿汉模式有点类似,只是在类装载的时候,SingletonHolder并没有被主动使用,只有显式调用getInstance方法是,才会调用装载SingletoHolder类,从而实例化instance,既实现了线程安全,又避免了同步带来的性能影响。
private static class SingletonHolder { private static final PostContentForResponse INSTANCE = new PostContentForResponse(); } private PostContentForResponse() {} public static final PostContentForResponse getInstance() { return SingletonHolder.INSTANCE; }
3) 理解单例注入和@Component注入在Spring管理的Bean对象构建流程:
对于一个单例类@Autowired注解方式添加,启动时会报空指针异常,因为static类对象是创建对象后,内存中还没有注入Bean信息,而且无法初始化Bean实例。
解决方法:例如@PostConstruct来对单例类中对象注入:
@Component public class RestTemplateUtil { public static RestTemplateUtil instance = new RestTemplateUtil(); @Autowired private RestTemplate restTemplate; private RestTemplateUtil() {} public static RestTemplateUtil getInstance() { return instance; } // 对单例类中对象注入 @PostConstruct public void init() { instance = this; instance.restTemplate = this.restTemplate; } }