java 单例的五种实现方式及其性能分析

作者:-SOLO- 时间:2023-05-10 10:50:51 

java 单例的五种实现方式及其性能分析

序言

在23种设计模式中,单例是最简单的设计模式,但是也是很常用的设计模式。从单例的五种实现方式中我们可以看到程序员对性能的不懈追求。下面我将分析单例的五种实现方式的优缺点,并对其在多线程环境下的性能进行测试。

实现

单例模式适用于资源占用较多的类,保证一个类只有一个实例即单例。通用的做法就是构造器私有化,提供一个全局的访问点,返回类的实例。

uml图:

java 单例的五种实现方式及其性能分析

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() {
return instance;
}
}

优点

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();
}
}

/**
* 当调用此方法的时候才初始化实例, 为了实现线程安全,需要使用同步方法
*
* @return
*/
public static synchronized SingleDemo2 getInstance() {
if (instance == null) {
 instance = new SingleDemo2();
}
return instance;
}
}

优点

1.只有使用这个类的时候才初始化实例,优化了资源利用率

缺点

1.为了实现线程安全,使用了同步方法获取,增加了访问的开销

注意

1.防止通过反射调用构造方法破解单例模式。
2.防止通过反序列产生新的对象。

3.双重检查

代码实现:


package com.zgh.gof23.singleton;

/**
* 双重检查
*
* @author zhuguohui
*
*/
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 RuntimeException();
}
}

/**
* 调用这个方法的时候,JVM才加载静态内部类,才初始化静态内部类的类变量。由于由JVM初始化,保证了线程安全性,
* 同时又实现了懒加载
* @return
*/
public static SingleDemo4 getInstance() {
return SingleDemo4Holder.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[] 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 count) {
this.count = count;
}

/**
* 当一个线程完成任务以后,调用一次这个方法
*/
public synchronized 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();
 }
}
}
}

结果

五种单例实现方式,在100个线程下,每个线程访问1千万次实例的用时.

Tables实现方式用时(毫秒)
1饿汉式13
2懒汉式10778
3双重检查15
4静态内部类14
5枚举12

(*注意:由于不同电脑之间的性能差异,测试的结果可能不同)

总结

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

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持! 

来源:http://blog.csdn.net/qq_22706515/article/details/74202814

标签:java,单例
0
投稿

猜你喜欢

  • 浅谈C#六大设计原则

    2023-05-02 16:29:58
  • 详解Android App中创建ViewPager组件的方法

    2023-07-12 00:46:14
  • Unity实现场景漫游相机

    2023-06-15 19:08:57
  • java中计算字符串长度的方法及u4E00与u9FBB的认识

    2022-07-15 18:28:20
  • Unity shader实现消融效果

    2023-03-06 01:06:44
  • c++ 预处理的图灵完备之引言

    2023-02-20 01:37:20
  • 详解Android中点击事件的几种实现方式

    2022-07-03 09:05:09
  • Java中的Kotlin 内部类原理

    2021-10-26 23:02:13
  • Java特性之注解和异常 Throwable

    2023-04-22 13:14:53
  • 在Winform程序中使用Spire.Pdf实现页面添加印章功能的实现

    2022-05-29 16:57:27
  • Android编程实现3D立体旋转效果的实例代码

    2023-04-01 13:40:50
  • 深入理解ThreadLocal工作原理及使用示例

    2022-02-27 19:24:14
  • Android开发中Activity的生命周期及加载模式详解

    2021-07-28 02:33:44
  • C++实现日期类的示例详解

    2022-07-29 04:19:24
  • 一步步教你如何创建第一个C#项目

    2021-08-15 04:41:56
  • Android中自定义控件的declare-styleable属性重用方案

    2021-08-30 13:16:29
  • 基于JAVA中Jersey处理Http协议中的Multipart的详解

    2021-06-15 13:43:57
  • 浅谈对Java双冒号::的理解

    2023-09-20 02:19:21
  • IOS开发向右滑动返回前一个页面功能(demo)

    2021-09-23 04:30:11
  • Java使用新浪微博API通过账号密码方式登陆微博的实例

    2023-09-23 05:35:38
  • asp之家 软件编程 m.aspxhome.com