在23种设计模式中,单例是最简单的设计模式,但是也是很常用的设计模式。从单例的五种实现方式中我们可以看到程序员对性能的不懈追求。下面我将分析单例的五种实现方式的优缺点,并对其在多线程环境下的性能进行测试。
实现
单例模式适用于资源占用较多的类,保证一个类只有一个实例即单例。通用的做法就是构造器私有化,提供一个全局的访问点,返回类的实例。
uml图:
这里写图片描述1.饿汉式
代码实现:
package com.zgh.gof23.singleton;
/** * 饿汉式 * @author yuelin * */public class SingleDemo { private static SingleDemo instance = new SingleDemo(); //私有化构造器 private SingleDemo() { //防止其他通过反射调用构造方法,破解单例 if (instance != null) { throw new RuntimeException(); } }//对外提供统一的访问点
public static SingleDemo getInstance() { 优点1.实例的初始化由JVM装载类的时候进行,保证了线程的安全性
2.实现简单方便3.实例的访问效率高缺点1.不能实现懒加载,如果不调用getInstance(),那么这个类就白白的占据内存,资源的利用率不高
注意1.防止通过反射调用构造方法破解单例模式。
2.防止通过反序列产生新的对象。2.懒汉式代码实现:
package com.zgh.gof23.singleton;
/**
* 懒汉式实现单例 * * @author zhuguohui * */public class SingleDemo2 { // 此处并不初始化实例 private static SingleDemo2 instance;private SingleDemo2() {
if (instance != null) { throw new RuntimeException();优点1.只有使用这个类的时候才初始化实例,优化了资源利用率
缺点1.为了实现线程安全,使用了同步方法获取,增加了访问的开销
注意1.防止通过反射调用构造方法破解单例模式。
2.防止通过反序列产生新的对象。3.双重检查代码实现:
package com.zgh.gof23.singleton;
/**
* 双重检查 * * @author www.vboyl130.cnzhuguohui * */public class SingleDemo3 { private static SingleDemo3 instance;private SingleDemo3() {
if (instance != null) { throw new RuntimeException(); } }public static SingleDemo3 getInstance() {
//第一重检查,提高效率 if (instance == null) { synchronized (SingleDemo3.class) { //第二重检查保证线程安全 if (instance == null) { instance = new SingleDemo3(); } } } return instance;优点1.实现懒加载
2.通过缩小同步区域和第一次检查提高访问效率缺点1.为了实现线程安全,使用了同步方法获取,增加了访问的开销
注意1.防止通过反射调用构造方法破解单例模式。
2.防止通过反序列产生新的对象。4.静态内部类代码实现:
/**
* 静态内部类实现单例 * * @author zhuguohui * */public class SingleDemo4 { private static SingleDemo4 instance;private static class SingleDemo4Holder {
private static final SingleDemo4 instance = new SingleDemo4(); }private SingleDemo4() {
if (instance != null) { throw new www.xyseo.net/ RuntimeException(); } }/**
* 调用这个方法的时候,JVM才加载静态内部类,才初始化静态内部类的类变量。由于由JVM初始化,保证了线程安全性, * 同时又实现了懒加载 * @return */ public static SingleDemo4 getInstance() { return www.yuheng119.com/ SingleDemo www.ysylcsvip.cn 4Holder.instance;优点1.即实现了线程安全,又实现了懒加载
缺点2.实现稍显复杂
5.枚举实现代码实现:
/**
* 枚举实现单例 * 枚举由JVM实现其的单例性 * @author zhuguohui * */public enum SingleDemo5 { INSTANCE;优点1.实现简单
2.线程安全3.天热对反射和反序列化漏洞免疫(由JVM提供)缺点2.不能实现懒加载
注意1.防止通过反射调用构造方法破解单例模式。
2.防止通过反序列产生新的对象。测试源码
public class APP {
public static void main(String[www.sb45475.com] args) {int threadCount = 100;
long start = System.currentTimeMillis(); final CountLock lock = new CountLock(threadCount); for (int i = 0; i < threadCount; i++) { new Thread(new Runnable() {@Override
public void run() { for (int j = 0; j < 10000000; j++) { //通过更换此处,来测试不同单例实现方式在多线程环境下的性能 SingleDemo5 demo = SingleDemo5.INSTANCE; } lock.finish(); } }).start();}
//等待所有线程执行完 lock.waitForWrok(); long end = System.currentTimeMillis(); System.out.println("总共耗时" + (end - start)); }为了统计所以线程执行完需要的时间,我写了一个工具类package com.zgh.gof23.singleton;
public class CountLock {
//线程的总数量 private int count;public CountLock(int www.dejiaylsmile.cn count) {
this.count = count; }/**
* 当一个线程完成任务以后,调用一次这个方法 */ public synchronized www.jnd3658.cn void finish() { count--; if (count == 0) { notifyAll(); } }/**
* 需要等待其他线程执行完的线程,调用此方法。 */ public synchronized void waitForWrok() { while (count > 0) { try { wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(www.wmylzc.cn); } } }结果
五种单例实现方式,在100个线程下,每个线程访问1千万次实例的用时.
Tables 实现方式 用时(毫秒)
1 饿汉式 132 懒汉式 107783 双重检查 154 静态内部类 145 枚举 12(*注意:由于不同电脑之间的性能差异,测试的结果可能不同)总结
如果需要懒加载就使用静态内部类方式,如果不需要就使用枚举方式。