您的位置:首页 > 职场人生

java面试常见模式之一单例模式学习详解

2016-08-14 20:44 411 查看
一. 概念

Ensure a class has oly one instance, and provide a globle point of access to it (确保某一个类只有一个实例,且自行实例化并向整个系统提供这个实例)

二. 使用场景

1.要生成唯一序列号的环境

2.创建一个对象需要消耗的资源过多,如访问数据库 IO等

3.项目需要一个共享的访问点或共享数据库,线程池

三 懒汉模式

非线程安全

即调用时再初始化实例对象

public class Singleton {

private static Singleton singleton = null;

private Singleton() {
}

public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}


非线程安全 这方式在高并发的时候有可能会产生多个singleton实例, 如果有2个线程T1,T2同时执行,当T1执行完if语句的时候,时间片段到,系统开始让T2执行,执行到第完成返回return 实例后,然后T1又开始执行,因为T1已完成做过判断此时并不知道singleton已被实例化,

所以singleton此时再次被实例化,这样你系统就有2个singleton对象。

2. 线程安全

解决1.中的出现两个实例问题。

public class Singleton {
private static Singleton singleton = null;

private Singleton() {
}

public synchronized static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}


加上了线程同步,这个时候确实能保证线程安全问题,但如果现在singleton已经被实例化了,如果10个线程同时访问,每次都要等待那么势必造成性能极大的消耗参考Singleton,双重校验版本

1. 双重校验

public class Singleton {

private static Singleton singleton = null;

private Singleton() {
}

public static Singleton getInstance() {

if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}


较上一段代码的基础上提升了不少性能,减少了不必要的等待,但是仔细一看说这代码有点问题,并不能保证线程的安全。

如果有T1,T2两个线程,T1线程运行第一个判断语句行发现singleton==null,就进入第synchronized 行,开始对singleton进行实例化,因为实例化中分为三步,第一步为对象开辟内存空间,第二步为对象初始化,第三步是把这个内存地址赋给singleton,但是因为java的内存模式允许无序写入,这样一来会导致第二步和第三步位置调换,那么这样一来就坏了,如果先允许第一步和第三步了,但是此时并没有对对象进行初始化,恰恰在此时T2进入了第一个判断语句行,经过判断singleton不为null,那么就会返回一个没有被初始化的对象。

4. 有序化

public class Singleton {
private **volatile** static Singleton4 singleton4 = null;

private Singleton() {
}

public static Singleton getInstance() {
if (singleton4 == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}


四. 饿汉式

即一开始就加载类,实例化对象

public class Singleton {

private static final Singleton SINGLETON5 = new Singleton5();// 直接定为final变量就不会出现多个实例

private Singleton() {
};

public static Singleton getInstance() {

return SINGLETON;
}
}


五. 静态内部类模式

解决饿汉模式着急创建对象,在加载时候消耗性能,而懒汉模式又存在线程安全问题

public class Singleton {
private static class SingletonManager {
private final static Singleton SINGLETON = new Singleton6();
}

public final static Singleton getSingleton6() {
return SingletonManager.SINGLETON;
}
}


六.枚举模式

enum Singleton7 {
SINGLE;
}


Java中,枚举不仅能够有字段,还能够有自己的方法,而且枚举实例的创建默认是线程安全的, 并且在任何情况下都是单例.之前的几种模式虽然外部不能实现,但是如果通过反序列化还是可以创建多个实例的,当然,如果重写了反序列化的readResolve()方法,也可以避免,不过相信一般也不会有人这么较劲非得要用这种方法来创建多个实例,枚举模式最大的优点就是写法简单粗暴

七. 单例模式总结

单例模式优点

1.在内存中只有一个实例,减少了内存开支,特别是对象需要频繁创建和销毁对象时,而且创建或销毁时性能优化点不大,这时候单例模式的优点就特别明显了

2.当一个对象产生需要比较多的资源时,则可以通过单例模式,来解决多个对象资源占用大的问题

3.可以避免对资源的多重占用

4.单例模式可以在系统设置全局的访问点,优化和共享资源访问

缺点

1.单例模式没有接口,扩展困难,一般都是通过直接修改源码扩展

2.避免单例模式中持有生命周期短暂的对象引用,容易造成内存泄露,如果持有此类对象,最好在使用后及时释放

3.单例模式与单一职责原则有冲突。一个类应该只实现一个逻辑,而不关心它是否单例,需不需要单例取决于环境,单例模式把“要单例”和业务逻辑融合再一个类中。

八. 单例模式和静态类区别

因静态类同样也是产生一个对象,相似度很高的哦。

1.面向对象中有三大特性继承,封装和多态,但是静态类是不可以继承的,所以从oo角度来说静态类并不符合面向对象,他们的类是不可以被覆盖,所以灵活性要比单例差的多

2.由于静态类的特殊他在编译器已经进行实例化了并不能提供懒加载模式

3.对于项目中如果进行单元测试,由于方法不能覆盖同样为测试带来了困难

4.由于静态类在编译器已经都被实例化,所以要比单例性能要快,如果只需要执行一些静态方法这个时候可以采用静态类
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 面试