Java单例模式在多线程环境下的性能测试对比实验分析
2017-07-18 21:46
686 查看
Java设计模式之单例模式
单例模式顾名思义就是在内存只有一个实例,是一个比较常用的设计模式,在很多的应用场景都有使用。关于他的UML图,应用场景,优点和缺点我就不介绍了,可以参考别人的文章或者文献等。实验目标:
- 验证几种单例模式是否线程安全
- 多线程条件下:传统技术实例化类的时间性能问题
- 多线程条件下:单例模式实例化类的时间性能问题
- 模拟场景进行实验的对比,用数据说话
单例模式模式之饿汉式 —线程安全
public class Singleton1{ private static final Singleton1 instance = new Singleton1(); /** * 构造函数设成私有,只允许获取实例的方法调用 */ private Singleton1(){ } public static Singleton1 getInstance(){ return instance; } }
验证是否饿汉式线程安全
public class Client { public static void main(String[] args) { // TODO Auto-generated method stub //开启100个线程获取对象实例 for(int i = 0;i<100;i++){ Person person = new Person(); person.start(); } } } /** * 模拟应用场景:例如:KTV房间里只有一个话筒:而且有很多人都要唱歌 多线程模拟多人拿起的话筒是不是一个话筒 */ class Person extends Thread{ @Override public void run(){ System.out.println("话筒的hashCode值:"+Singleton1.getInstance().hashCode()); } }
结果如图所示
public class Singleton2 { private static Singleton2 instance = null; //构造函数私有 private Singleton2(){ } //静态的获取对象实例的方法 public static synchronized Singleton2 getInstance(){ if(instance == null){ instance = new Singleton2(); //如果对象为null则新建,否则直接返回 } return instance; } }
测试同上
单利模式之静态内部类
public class Singleton3 { private static class SingletonHolder{ private static final Singleton3 instance = new Singleton3(); } private Singleton3(){ } //静态的获取对象实例的方法 public static final Singleton3 getInstance(){ return SingletonHolder.instance; } }
验证结果:线程安全
单例模式之双重校验锁
public class Singleton4 { private volatile static Singleton4 instance; //构造 private Singleton4(){ } //静态的获取对象实例的方法 public static Singleton4 getInstance(){ // 两重判断, 第一重是为了避免不必要的同步,因为频繁的锁会降低系统的性能 // 第二重是在instance在null情况下才会创建实例 if(instance == null){ synchronized (Singleton4.class) { if(instance == null){ instance = new Singleton4(); } } } return instance; } }
测试结果:线程安全
单例模式之枚举:线程安全
public enum Singleton5 { INSTANCE; }
同理测试:结果线程安全
几种单例模式的时间消耗
package com.jinwen.singleton; import java.util.concurrent.CountDownLatch; public class Client { private static int count = 100; //规定的线程数 private static long jobCountPerThread = 1000L; //每个线程作业数 public static void main(String[] args) throws InterruptedException{ // TODO Auto-generated method stub test1(); // 饿汉式测试 test2(); //懒汉式 test3(); //静态内部类 test4(); //双重校验锁 test5(); //枚举 test6(); //在同一种情况下直接传统技术new一个对象出来 } /** * 饿汉式在多线程环境下的测试函数 */ public static void test1() throws InterruptedException{ long startTime = System.currentTimeMillis(); //开始时间 final CountDownLatch countDownLatch = new CountDownLatch(count); for(int i=0;i<count;i++){ new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < jobCountPerThread; i++) { Singleton1 s1 = Singleton1.getInstance(); } countDownLatch.countDown(); } }).start(); } countDownLatch.await(); long endTime = System.currentTimeMillis(); System.out.println("饿汉式总消耗时间:"+(endTime-startTime)+"ms"); } /** * 懒汉式 多线程环境下的测试 */ public static void test2() throws InterruptedException{ long startTime = System.currentTimeMillis(); //开始时间 final CountDownLatch countDownLatch = new CountDownLatch(count); for(int i=0;i<count;i++){ new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < jobCountPerThread; i++) { Singleton2 s2 = Singleton2.getInstance(); } countDownLatch.countDown(); } }).start(); } countDownLatch.await(); long endTime = System.currentTimeMillis(); System.out.println("懒汉式总消耗时间:"+(endTime-startTime)+"ms"); } /** * 静态内部类 多线程环境下的测试 */ public static void test3() throws InterruptedException{ long startTime = System.currentTimeMillis(); //开始时间 final CountDownLatch countDownLatch = new CountDownLatch(count); for(int i=0;i<count;i++){ new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < jobCountPerThread; i++) { Singleton3 s3 = Singleton3.getInstance(); } countDownLatch.countDown(); } }).start(); } countDownLatch.await(); long endTime = System.currentTimeMillis(); System.out.println("静态内部类总消耗时间:"+(endTime-startTime)+"ms"); } /** * 双重校验锁 多线程环境下的测试 */ public static void test4() throws InterruptedException{ long startTime = System.currentTimeMillis(); //开始时间 final CountDownLatch countDownLatch = new CountDownLatch(count); for(int i=0;i<count;i++){ new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < jobCountPerThread; i++) { Singleton4 s4 = Singleton4.getInstance(); } countDownLatch.countDown(); } }).start(); } countDownLatch.await(); long endTime = System.currentTimeMillis(); System.out.println("双重校验锁总消耗时间:"+(endTime-startTime)+"ms"); } /** * 枚举 多线程环境下的测试 */ public static void test5() throws InterruptedException{ long startTime = System.currentTimeMillis(); //开始时间 final CountDownLatch countDownLatch = new CountDownLatch(count); for(int i=0;i<count;i++){ new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < jobCountPerThread; i++) { Singleton5 s5 = Singleton5.INSTANCE; } countDownLatch.countDown(); } }).start(); } countDownLatch.await(); long endTime = System.currentTimeMillis(); System.out.println("枚举总消耗时间:"+(endTime-startTime)+"ms"); } /**********************多线程环境下直接new一个对象********************************/ public static void test6() throws InterruptedException{ long startTime = System.currentTimeMillis(); //开始时间 final CountDownLatch countDownLatch = new CountDownLatch(count); for(int i=0;i<count;i++){ new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < jobCountPerThread; i++) { Singleton s = new Singleton(); } countDownLatch.countDown(); } }).start(); } countDownLatch.await(); long endTime = System.currentTimeMillis(); System.out.println("传统技术直接new总的消耗时间:"+(endTime-startTime)+"ms"); } } class Singleton{ public Singleton(){ } } /** * 模拟应用场景:例如:KTV房间里只有一个话筒:而且有很多人都要唱歌 多线程模拟多人拿起的话筒是不是一个话筒 */ class Person extends Thread{ @Override public void run(){ //System.out.println("话筒的hashCode值:"+Singleton5.INSTANCE.hashCode()); } }
测试结果:通过不断的调整的count和jobCountPerThread的数,通过不断的进行实验,相信大家应该获得和我相类似的结果
实验序号 | 线程数 | 作业数 |
---|---|---|
1 | 10 | 100000 |
2 | 100 | 100000 |
3 | 200 | 100000 |
4 | 500 | 100000 |
5 | 1000 | 100000 |
6 | 10 | 10000 |
7 | 100 | 1000 |
8 | 1000 | 1000 |
结果分析:当在一个线程中调用对象实例的次数越多,也就是JobCountPerThread越大,使用单例模式显然更好,当然懒汉单例模式除外,当线程数高,JobCountPerThread不高,也就是我们在实际生活高并发的情况,单例模式不会比传统的新建对象的性能好,当高并发情况下,大部分单例模式甚至会更差些。
总结:我们在学习的时候应该实事求是,严谨求学,模式只有在适合的场景下用对了才会发挥出更好的性能,否则情况可能会更差。最后,我还是学生,希望能有大神多多给我一些意见,指出不对的地方。
代码的地址:https://github.com/jinchen92/DesignPattern
相关文章推荐
- Memcached Redis Membase性能测试对比分析
- PHP4和PHP5性能测试和对比 测试代码与环境
- 常用Java性能测试工具的分析与对比
- Memcached Redis Membase性能测试对比分析
- PHP4和PHP5性能测试和对比 测试代码与环境
- 性能测试环境与真实环境的对比
- 【原创】性能测试之——网络环境分析
- 数据库性能对比测试实验
- 关于侯垒的自增字段和GUID字段性能对比文章的一些自己的分析(没有测试,纯粹分析)
- hbase性能测试对比分析
- windows环境常用网络命令测试和分析(51cto实验01~02)
- .Net Core内存回收模式及性能测试对比分析
- Go1.5正式版程序性能分析小积累,实验环境windows64
- 常用Java性能测试工具的分析与对比
- 多进程多线程环境下的同步机制性能测试
- 使用分析函数和不使用分析函数的性能对比测试
- 多线程下对java并发集合测试 性能分析
- Go1.5正式版程序性能分析小积累,实验环境windows64
- 多线程下对java并发集合测试 性能分析
- 多线程下对java并发集合测试 性能分析