您的位置:首页 > 其它

单例模式

2017-06-08 15:55 43 查看

第一种 懒汉 线程不安全

public class Singleton {

private static Singleton instance;

private Singleton() {
}
//静态工厂方法
public static Singleton getInstance() {

if (instance == null)
instance = new Singleton();
return instance;
}
}


这种写法lazy loading很明显,等到第一次使用时再创建实例,因此称为懒汉。问题是该写法没有考虑线程安全问题,并发环境下可能会出现多个Singleton实例,在多线程下不能正常工作。但是提供了单例的基本思路:构造器私有。将构造器限制为private能够避免类在外部被实例化。

以下三种是对懒汉模式的改造,保证线程安全

第二种 懒汉 synchronized 保证线程安全

public class Singleton {

private static Singleton instance;

private Singleton() {
}
//静态工厂方法 采用同步
public static synchronized Singleton getInstance() {

if (instance == null)
instance = new Singleton();
return instance;
}
}


该写法能够保证线程安全,但是效率很低。不论instance是否被实例化,当调用getInstance获取实例的时候,都会加锁。

第三种 懒汉 双重校验锁

public class Singleton {

private volatile static  Singleton instance;

private Singleton() {
}

public static Singleton getInstance() {

if (instance == null) {//第一次判断
synchronized (Singleton.class) {
if (instance == null)//第二次判断
instance = new Singleton();
}
}

return instance;
}
}


1.为何synchronized不加在getInstance方法上,而是移到方法体内?

将synchronized写到方法体内,是为了降低锁的粒度。考虑存在这样的情况:Singleton类中还会存在其他的static方法如f(),当synchronized直接加到getInstance方法上的时候,就会对整个Singleton类加锁,其他程序想要调用类中f(),无法执行只能等待getInstance方法执行完再调用。

将synchronized写进方法内,只有instance实例未被实例化的时候,才会将整个Singleton类进行加锁。所以将synchronized写到方法体内是为了不影响其他方法的调用。

2.为何会进行两次instance==null的判断?去掉第一个可以吗?去掉第二个可以吗?

去掉第一个也可以,但是不推荐。如果去掉第一个if判断,那么当调用getInstance方法的时候,无论instance是否被实例化,都会对整个类进行加锁,不去掉第一个的话,只有instance== null的时候,才会对整个类加锁。

去掉第二个不可以。很可能在第一个if判断的时候,instance== null成立,等到执行到同步块中的时候,instance可能已经被其他线程实例化了。所以需要再加一次判断。

3.关于volatile的作用?

volatile的作用是避免指令的重排序。初始化一个实例:instance= newInstance()在字节码中有四个步骤:

1.申请内存空间

2.初始化默认值

3.执行构造器方法

4.连接引用和实例

这四个步骤后面两个可能会出现重排序,1234或者1243,1243出现的结果就是造成未初始化完全的对象会被发布然后被使用。volatile可以禁止指令的重排序,从而避免这个问题。

第四种 懒汉 静态内部类

public class Singleton {

private static class LazyHolder {

private static final Singleton INSTANCE = new Singleton();
}

private Singleton() {}

public static final Singleton getInstance() {

return LazyHolder.INSTANCE;
}
}


第五种 饿汉 非延迟加载

public class Singleton {
//加载类的时候会初始化static的instance,
private static Singleton instance = new Singleton();

private Singleton() {}

public static Singleton getInstance() {
return instance;
}
}


instance在类装载时就被实例化,因此是天生安全。

第六种 饿汉

public class Singleton {

private static Singleton instance;

static {
instance = new Singleton();
}

private Singleton() {
}

public static Singleton getInstance() {
return instance;
}
}


该种写法与第五种类似,同样都是在类初始化的时候创建静态对象,因此也是线程安全的。

第七种 枚举

public enum Singleton {

INSTANCE;
}


枚举实现单例模式,不仅可以避免多线程问题,还可以防止反序列化建立新对象。枚举单例模式有点类似饿汉模式,没有实现lazy loading。

饿汉和懒汉的区别:

饿汉保证类一旦加载完成,就把单例实例化,等到getInstance()时,单例已经存在。饿汉天生线程安全,因此可直接用于多线程。

懒汉顾名思
4000
义,lazy loading为 延迟加载,等到getInstance()时,再去实例化单例。

懒汉本身不是线程安全的,但是可以通过加同步,静态内部类,双重锁校验的方式,人为实现线程安全。

资源加载的性能:

饿汉模式下当类加载完成后,也完成了单例的实例化,单例就会存在于内存中,不管后面会不会用到,都会占用一定内存。如果该单例占用资源很多的情况下,采用饿汉模式,会导致性能降低。但是由于单例已经初始化完成,因此在第一次调用的时候速度会非常快。

懒汉模式下,延迟加载,对于占用很多资源的单例比较实用。等到使用该单例时再进行加载,会导致性能延迟。

从速度和反应角度看,饿汉模式较优;从资源利用率看,懒汉模式较优。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  单例模式 singleton