您的位置:首页 - 教程 - Java - 正文
Java 多线程编程中单例的实现
对于普通单线程单例来说,较为容易,只要避免创建多个对象即可,代码如下:

public class Singleton {
private static Singleton singleton = null;
public static Singleton getInstance(){
if(singleton==null){
singleton = new Singleton();
}
return singleton;
}
}
这样既可以避免创建多个对象消耗系统资源,也可以达到惰性加载,即在使用时才会创建对象;

在单线程时,该类可以很完美的解决单例问题,但是到了多线程,该类将会出现问题:

当两个现成A和B 同时调用单例获得实例时,若A进入getInstance()方法,判断当前singleton为空,则进入singleton=new Singeton(),并返回singleton,但是当A进入时,B同时调用,B也会判断singleton为空,因为A进入后并没有初始化完成,所以B同样会进入初始化代码,进行初始化并返回singleton,这样就出现问题了,并没有实现单例操作。

所以在多线程环境下,我们需要对该代码进行一点修改,那就是给getInstance方法加上同步锁,使A和B不能同时进入该方法内,代码如下:

public class Singleton {
private static Singleton singleton = null;
public static synchronized Singleton getInstance(){
if(singleton==null){
singleton = new Singleton();
}
return singleton;
}
}
经过修改后,我们发现在出现之前A和B 同时访问getInstance方法时,若A先进入getInstance方法,由于sychronized的存在,B是不可能进入该方法的,会在A线程执行完成之前将该方法进行加锁操作,执行完成之后才会允许B进入该方法,而当B进入方法时,singleton已经不再是null,直接返回单例singleton对象;

但是实际上,若多个线程调用时,同步锁的存在,很大程度上影响程序性能,所以后来有人提出double-checked blocking方法,来降低性能影响:



还有一种方法,叫做double-check blocking,代码如下:

public class Singleton {
private static Singleton singleton = null;
public static Singleton getInstance(){
if(singleton==null){
synchronized (singleton){
if(singleton==null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
经过此次修改,当线程进入getInstance方法后,将会先判断singleton是否为空,这样减少了进入同步块所花费的资源,降低了资源消耗,提高了性能,

似乎这样修改以后,即不会造成同步锁消耗资源,也不会由于多线程同时进入创建多个对象,但是,实际上,从更深一层来讲,站在jvm的层面来说,这样的代码仍可能发生问题:

由于在执行singleton = new Singleton();时,jvm是分两步进行的,先是为singleton预留空间,直接赋值给instance,然后才会初始化singleton,创建singleton实例,

如果A先进入singleton = new sSingleton(),但是只是分配了空间,并没有初始化完成,就返回singleton,那么当B线程进入时,singleton已经不为null,那么将直接返回已有的singleton,若在singleton真正初始化之前就使用的话,问题就来了,所有 多线程单例模式,又出现了新的解决办法:

public class Singleton{
private static c lass SingletonContainer{
private static Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return SingletonContainer.instance;
}

}
  该方法为通过内部类实现多线程环境中的单例,

JVM内部机制能够保证当一个类被加载时,这个类的加载过程是线程安全的,当我们第一次调用getInstance方法时,JVM能够保证instance只被创建一次,并且保证初始化完成,这样我们就不再需要担心instance没有被创建完成了,同时实现了惰性加载单例

评论: