同步方法与同步代码块的效率比较

目录

1 代码介绍

1.1 全局变量

1.2 因式分解代码

1.3 细粒度同步代码(同步代码块)

1.4 粗粒度同步代码(同步方法)

2 性能测试

2.1 细粒度测试(同步代码块)

2.2 粗粒度测试(同步方法)

3 总结


在《java并发编程实战》中提到同步方法在并发量大的情况下效率很低,但是为了保证程序的安全性,采用同步锁又是不得不考虑的问题,那有没有办法做到安全性和高效率同时实现呢?书中提到了可以用同步代码块取代对整个方法的同步,仅对方法中操作共享变量的代码加同步锁,这样就能使没有获取到锁的线程能执行同步代码块之前的代码,进而提高程序性能。

1 代码介绍

这里采用一个因式分解的例子,用户请求传入一个int数值,程序进行因式分解,然后将结果存储在响应头中返回。为了对程序进行优化,会对上一次因式分解的结果存储在全局变量中,若传入的待分解值等于上次分解的值,则直接返回结果。

1.1 全局变量

上一次分解的值和分解结果。

    private int lastNumber;
    private List<Integer> lastFactors;

1.2 因式分解代码

    private List<Integer> factor(int num) {
        List<Integer> factors = new ArrayList<>();
        for (int i = 2; i <= num; i++) {
            while (num % i == 0) {
                factors.add(i);
                num = num / i;
            }
        }
        return factors;
    }

1.3 细粒度同步代码(同步代码块)

仅对共享变量的操作进行了同步。

    @RequestMapping("/performanceTest")
    public synchronized void performanceTest(HttpServletResponse response, int number){
        List<Integer> factors = null;
        synchronized (this){
            if (number == lastNumber){
                factors = lastFactors;
            }
        }

        if (factors == null){
           factors = factor(number);
            synchronized (this){
                lastNumber = number;
                lastFactors = factors;
            }
        }

        response.setHeader("result",factors.toString());
    }

1.4 粗粒度同步代码(同步方法)

对整个方法进行同步。

    @RequestMapping("/performanceTest2")
    public synchronized void performanceTest2(HttpServletResponse response, int number){
        List<Integer> factors = null;
        if (number == lastNumber){
            factors = lastFactors;
        }

        if (factors == null){
            factors = factor(number);
                lastNumber = number;
                lastFactors = factors;
        }

        response.setHeader("result",factors.toString());
    }

 

2 性能测试

此次测试采用apache的Jmeter框架,使用http的post请求进行测试,测试的结果通过汇总报告来进行展示,汇总报告的相关参数介绍见:汇总报告。设置访问线程数为3000个,这3000个线程被设置在1秒内全部启动。这个过程会循环3次,意味着此次测试会在3秒内启动9000个线程。

2.1 细粒度测试(同步代码块)

9000个线程的请求和响应总共花费了10秒钟,所有请求响应时长的平均值为821毫秒,最大值3313毫秒,最小值9毫秒。每秒能处理的请求个数(吞吐量)为912.3个,异常率为11.93%。

注:有些人可能看到平均响应时间为821毫秒,觉得9000*821那肯定是大于总执行时间10秒的啊,是不是出了什么问题?其实不然,当一个线程获取到了锁,之后所有产生的线程都必须等待,而线程是并行产生的,所有线程从发出请求到获得响应的时间的总和是绝对要大于10秒的。

2.2 粗粒度测试(同步方法)

9000个线程的请求和响应总共花费了12秒钟,所有请求响应时长的平均值为2168毫秒,最大值9803毫秒,最小值8毫秒。每秒能处理的请求个数(吞吐量)为712.0个,异常率为14.70%。

3 总结

从上面可以看出,对一个方法的同步机制进行小小的改动,在1秒并发3000的情况下同步代码块相比同步方法:

  • 平均响应时间:后者是前者的2.6倍。
  • 吞吐量(每秒处理的请求数):前者是后者的1.2倍。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值