在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 饿汉式 13
2 懒汉式 10778
3 双重检查 15
4 静态内部类 14
5 枚举 12
(*注意:由于不同电脑之间的性能差异,测试的结果可能不同)

总结

如果需要懒加载就使用静态内部类方式,如果不需要就使用枚举方式。

实例的初始化由JVM装载类的时候进行,保证了线程的安全性的更多相关文章

  1. jvm(1)类的加载(三)(线程上下文加载器)

    简介: 类加载器从 JDK 1.0 就出现了,最初是为了满足 Java Applet 的需要而开发出来的. Java Applet 需要从远程下载 Java 类文件到浏览器中并执行. 现在类加载器在 ...

  2. 苹果新的编程语言 Swift 语言进阶(十一)--实例的初始化与类的析构

    一 .实例的初始化          实例的初始化是准备一个类.结构或枚举的实例以便使用的过程.初始化包括设置一个实例的每一个存储属性为一个初始值,以及执行任何其它新的实例能够使用之前需要的设置或初始 ...

  3. Java类、实例的初始化顺序

    今晚是阿里巴巴 2013 校园招聘的杭州站笔试.下午匆忙看了两张历年试卷,去现场打了瓶酱油. 题目总体考察点偏基础,倒数第二题(Java 附加题)比较有趣,考察了 Java 初始化机制的细节,在此摘录 ...

  4. Java 类的实例变量初始化的过程 静态块、非静态块、构造函数的加载顺序

    先看一道Java面试题: public class Baset { private String baseName = "base"; // 构造方法 public Baset() ...

  5. JDK8中JVM对类的初始化探讨

    在<深入理解Java虚拟机>(第二版,周志明著)中,作者介绍了JVM必须初始化类(或接口)的五种情况,但是是针对JDK7而言的. 那么,在JDK8中,这几种情况有没有变化呢?(我猜测应该会 ...

  6. JDK 8 - JVM 对类的初始化探讨

    在<深入理解 Java 虚拟机>(第二版,周志明著)中,作者介绍了 JVM 必须初始化类(或接口)的五种情况,但是是针对 JDK 7 而言的. 那么,在 JDK 8 中,这几种情况有没有变 ...

  7. 异常将上下文初始化事件发送到类的侦听器实例.[org.springframework.web.context.ContextLoaderListener] org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined in class p

    严重: 异常将上下文初始化事件发送到类的侦听器实例.[org.springframework.web.context.ContextLoaderListener]org.springframework ...

  8. jvm(1)类的加载(二)(自定义类加载器)

    [深入Java虚拟机]之四:类加载机制 1,从Java虚拟机的角度,只存在两种不同的类加载器: 1,启动类加载器:它使用C++实现(这里仅限于Hotspot,也就是JDK1.5之后默认的虚拟机,有其他 ...

  9. JVM调用类的main方法的过程

    jvm先装载类,再链接类,再初始化类(以代码的文本顺序执行类变量初始化器.类静态初始化方法或接口的属性(field)初始化器),完成类的初始化后,才执行类的main方法.在链接过程中,可以静态链接(解 ...

随机推荐

  1. jQuery的过滤器总结

    1.内容过滤器 $(function () { // $("a:contains('标签')").css("color","green") ...

  2. 接雨水12 · Trapping Rain Water12

    [抄题]: Given n non-negative integers representing an elevation map where the width of each bar is 1, ...

  3. Yii2 windows 安装步骤

    安装 Yii2 高级版应用 基础版可以看官方文档 今天安装了YII2高级版应用 写下步骤 留个记号,以备查询! 在 Windows 中,你首先需要下载并运行 Composer-Setup.exe 查看 ...

  4. QuartJob的CronExpressionString规则详解

    字段 允许值 允许的特殊字符    秒   0-59   , - * /    分   0-59   , - * /    小时 0-23   , - * /    日期 1-31   , - * ? ...

  5. [Training Video - 2] [Java Introduction] [Operator, Loops, Arrays, Functions]

    Operator : While Loop : For Loop :  Arrays : Code : public class FirstJavaClass { public static void ...

  6. Mac 安装GCC

    OS X上安装Homebrew和GCC的图文攻略 2016年08月31日 11:21:27 白马负金羁 阅读数:11380 标签: OS XGCCHomebrew 更多 个人分类: 应用技巧   版权 ...

  7. Smart Pointe

    http://blog.chinaunix.net/uid-625789-id-2720884.html

  8. Linux文件备份

    1.tar -P是否保留根目录 -t查看压缩文件内容 -N 201401010备份日期以后 [root@localhost /]# tar -zcPf /tar/data2.tar.gz /etc/* ...

  9. MFC中的主窗口修改标题

    MFC中的主窗口修改标题 如何去掉“无标题”1.在主程序中的InitInstance(): m_pMainWnd->SetWindowText("你要显示的东西如果不想显示置空就行&q ...

  10. CGLIB实现动态代理

    JDK动态代理和CGLIB字节码生成的区别? * JDK动态代理只能对实现了接口的类生成代理,而不能针对类 * CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法   因为是继承 ...