前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >并发中atomic BUG分享

并发中atomic BUG分享

作者头像
FunTester
发布2023-08-10 15:38:50
1050
发布2023-08-10 15:38:50
举报
文章被收录于专栏:FunTesterFunTester

在使用Java做性能测试的过程中,遇到过很多自己抗自己的坎儿。在经历过风风雨雨之后,自认为已经是个并发编程的老司机,没想到前两天又丢进了同一个坑中。

「保持操作的原子性!!!」

「保持操作的原子性!!!」

「保持操作的原子性!!!」

重要的事情写三遍。

事情是这样,要写一个脚本,需求是对所有的用户进行初始化(包含HTTP请求),为了加速,自然选择了并发实现。(隐藏需求:每个用户都需要初始化,但可以重复初始化)

为了达到动态调整初始化的QPS,选择了动态QPS实现这个功能。

下面是伪代码的实现:

代码语言:javascript
复制
package com.funtest.temp


class AtomicTest {

    static List<User> users

    static class User {

        String token

        int uid


        void init() {
            //用户初始化
        }
    }

}

初步的思路是使用之前储备的顺序(伪随机)方法:

代码语言:javascript
复制
    /**
     * 随机选择某个对象
     *
     * @param list
     * @param index 自增索引
     * @param <F>
     * @return
     */
    public static <F> F random(List<F> list, AtomicInteger index) {
        if (list == null || list.isEmpty()) ParamException.fail("数组不能为空!");
        return list.get(index.getAndIncrement() % list.size());
    }

然后通线程安全的随机方法,实现每次请求的时候都使用不同的用户。然后通过识别index的实际地增值来判断是否完成所有用户的遍历。

代码语言:javascript
复制
    static void main(String[] args) {
        AtomicInteger index = new AtomicInteger()
        def test = {
            SourceCode.random(users, index).init()
            if (index.get() > users.size()) FunQpsConcurrent.stop()
        }
        new FunQpsConcurrent(test, "原子操作BUG演示").start()
    }

但是BUG就来了,当我在测试的发现static List<User> users最后的几个用户始终无法完成用户的初始化。后来经过简单排查,发现了自己的问题。

「用了线程安全类,并不是就安全」

因为线程安全类在原子操作中是安全的,我下面吧test{}里面的重新分享一下。

代码语言:javascript
复制
        def test = {
       1.   def random = SourceCode.random(users, index)
       2.   random.init()
       3.   if (index.get() > users.size()) FunQpsConcurrent.stop()
        }

当第1行执行完,index已经递增了。但是其他线程执行到第3行,拿到已经递增的index值,然后判断执行了退出程序。

处理方法3种:

  1. 优化stop()方法,因为一旦index值到达阈值,就强制终止了执行。实际应该等到test{}全部执行完再执行推出程序。
  2. 额外增加一个java.util.concurrent.atomic.AtomicInteger对象,用来统计执行完成的用户数,而不是随机获取到用户数量。
  3. 使用线程安全的集合类对象,例如java.util.concurrent.LinkedBlockingQueue,然后poll()为空再执行退出方法。

今天的分享就到这里,其实Java在做性能测试方面还是简单的,多使用多踩坑,很容易掌握的。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-08-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 FunTester 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档