Java单实例模式_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > Java单实例模式

Java单实例模式

 2013/7/10 3:35:27  donlianli  程序员俱乐部  我要评论(0)
  • 摘要:前言:代码简洁与性能高效无法两全其美,本文章专注于大并发程序的性能,如果您追求代码简洁,本文章可能不太适合,因为本文章主要讨论如何写出在高并发下也能运行很好的代码。并文章属于Java并发编程实战中例子。但结合实际场景进行了阐述。通常,我们如果写一个单实例模式的对象,一般会这样写:写法一:publicclassSingleton{privatestaticfinalSingletoninstance=newSingleton();/***防止其他人new对象*/privateSingleton(
  • 标签:Java 模式 实例

前言:
代码简洁与性能高效无法两全其美,本文章专注于大并发程序的性能,如果您追求代码简洁,本文章可能不太适合,因为本文章主要讨论如何写出在高并发下也能运行很好的代码。

?

并文章属于Java并发编程实战中例子。但结合实际场景进行了阐述。

通常,我们如果写一个单实例模式的对象,一般会这样写:

写法一:

?

class="java" name="code">public class Singleton {
	private static final Singleton instance = new Singleton();
	/**
	 * 防止其他人new对象
	 */
	private Singleton(){
		System.out.println("init");
	}
	public static Singleton getInstance(){
		return instance;
	}
}

?

?

这种方式叫饥饿式单实例,意思是说,不管你用不用这个类的方法,我都把这个类需要的一切资源都分配好。但这样写有一个问题,就是如果这类需要的资源比较多,在系统启动的时候,就会很慢。

因此要求有懒汉式单实例,于是就出现了第二中写法,

写法二:

?

public class Singleton {
	private static Singleton instance = null;
	/**
	 * 防止其他人new对象
	 */
	private Singleton(){
		System.out.println("init");
	}
	public static Singleton getInstance(){
		if(instance == null){
			instance = new Singleton();
		}
		return instance;
	}
}

?这种方式叫懒汉式单实例,即通常所说的延迟加载。这样,在系统启动的时候,不会加载类所需要的各种资源,只有真正使用的时候才去加载各种资源。

?

但这种方法马上就可以看出问题,因为在多线程情况下,可能会导致重复初始化的问题(不明白这个道理,那您需要补充一下同步及多线程知识了)。于是有了改进版,即目前网上比较流行的写法。

写法三:

?

public class Singleton {
	private static Singleton instance = null;
	/**
	 * 防止其他人new对象
	 */
	private Singleton(){
		System.out.println("init");
	}
	public static synchronized Singleton getInstance(){
		if(instance == null){
			instance = new Singleton();
		}
		return instance;
	}
}

?加上关键字synchronized,可以保证只有一个线程在执行这个方法。这个方法至此应该说是比较完美的了,但是,专家不这么认为,在高并发多线程的访问系统中,synchronized关键字会让程序的吞吐量急剧下降,因此,在高并发系统中,应该尽量避免使用synchronized锁。

但这并不能难住我们聪明的软件工程师,有人便写出了双重锁的程序。方法如下:

写法四:

public class Singleton {
	private static Singleton instance = null;
	/**
	 * 防止其他人new对象
	 */
	private Singleton(){
		System.out.println("init");
	}
	public static  Singleton getInstance(){
		if(instance == null){
			synchronized(Singleton.class){
				if(instance == null){
					instance = new Singleton();
				}
			}
		}
		return instance;
	}
}

这样,通常获得单实例引用是没有锁的,只有第一次初始化时才会加锁,而且如果多个线程进入临界区区后,理论上只有第一个进入临界区的线程才会初始化对象,之后进入临界区的线程因为之前的线程已经初始化,就不会再次进行初始化。

但专家怎么说呢?这个代码有问题。首先,这个程序对同步的应用很到位,即当进入synchronied区,只有一个线程在访问Singleton类。但却忽略了变量的可见性。因为在没有同步的保护下,instance的值在多个线程中可能都是空的,因为即便第一个线程对类进行了初始化,并把类的引用赋值给了instance变量,但也不能保证instance变量的值对其他线程是可见的,因为变量instance没有采用同步的机制。

在java5之后,可以在instance前面添加volatile关键字来解决这个问题,但是这种双重锁的方式已经不建议使用。

?

那么,看看大师推荐的写法吧,见 Java Concurrency In Practice的List 16.6代码:

写法五:

public class Singleton {
	private static class SingletonHolder {
        public static Singleton resource = new Singleton();
    }
    public static Singleton getResource() {
        return  SingletonHolder.resource ;
    }
    
    private Singleton(){
    	
    }
}

?

综上各种写法,发现写法一虽然在启动时会让系统启动的慢一些,但却不失为一种简洁而高效的写法,当然,如果确实对系统启动时的速度要求高的话,则应该考虑写法五了。

另外,其实单实例方法还有好多种,在effective Java中有写到:

写法六:

public class Singleton {
	public static final Singleton INSTANCE = new Singleton();
	
	private Singleton(){}
	
	public void method(){
		//...
	}
	public static void main(String[] a){
		//调用方法。
		Singleton.INSTANCE.method();
	}
}

?写法七:

/**
 * 利用枚举巧妙创建单实例
 */
public enum Singleton {
	INSTANCE;
	public void method(){
		//...
	}
	public static void main(String[] a){
		//调用方法。
		Singleton.INSTANCE.method();
	}
}

?另外,双重锁的方式,在加上volatile关键字后,也是高效安全的写法。

写法八:

public class Singleton {
	private static volatile Singleton instance = null;
	/**
	 * 防止其他人new对象
	 */
	private Singleton(){
		System.out.println("init");
	}
	public static  Singleton getInstance(){
		if(instance == null){
			synchronized(Singleton.class){
				if(instance == null){
					instance = new Singleton();
				}
			}
		}
		return instance;
	}
}

?

?

其实,在今天spring大行其道的天下,单实例需求已经不多,spring中的bean默认都是单实例。但是要做一些app程序或者开发一个产品时,这种模式还是很重要的。综上所述,我个人比较推荐写法五和写法一,写法七怎么看着也别扭。

另外感谢大家的讨论,这个话题先到这儿吧,我写本文章的主要目的是为了纠正写法三的错误。不知道你的项目中是否还存在写法三的代码呢?

? 对性能感兴趣?请查看 并发编程 系列文章,持续更新中 对这类话题感兴趣?欢迎发送邮件donlianli@126.com 关于我:邯郸人,擅长Java,Javascript,Extjs,oracle sql。 更多我之前的文章,可以访问:http://hi.baidu.com/donlian

?

发表评论
用户名: 匿名