Java单例模式在多线程环境下的性能测试对比实验分析

来源:互联网 发布:大数据 书 编辑:程序博客网 时间:2024/05/16 05:49

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

阅读全文
0 0
原创粉丝点击