首页 > 其他分享 >并发中atomic BUG分享

并发中atomic BUG分享

时间:2023-08-07 21:33:48浏览次数:50  
标签:index users list 原子 并发 线程 atomic 操作 BUG

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

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

重要的事情写三遍。

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

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

下面是伪代码的实现:

package com.funtest.temp


class AtomicTest {

    static List<User> users

    static class User {

        String token

        int uid


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

}

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

    /**
     * 随机选择某个对象
     *
     * @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的实际地增值来判断是否完成所有用户的遍历。

    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{}里面的重新分享一下。

        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在做性能测试方面还是简单的,多使用多踩坑,很容易掌握的。

知识补充:

在计算机科学中,"atomic"(原子)是指一种不可再分割的操作单位。原子操作是指一个操作或一组操作在执行过程中不会被中断,要么全部执行完成,要么完全不执行,没有中间状态。这样的操作能够确保在多线程或并发环境下,操作的执行是线程安全的,不会发生竞态条件或数据不一致的问题。

原子操作通常用于对共享资源进行修改或访问的场景,比如多线程修改同一个变量、更新共享数据结构等。它可以保证操作的完整性,不受其他线程的干扰,从而避免并发冲突。

在编程中,原子操作通常由特定的原子指令或者使用锁(如互斥锁或读写锁)来实现。在现代编程语言中,也提供了原子操作的支持,比如Java中的AtomicInteger等,这些特殊的数据结构或函数允许开发者进行原子操作,确保线程安全性。

标签:index,users,list,原子,并发,线程,atomic,操作,BUG
From: https://blog.51cto.com/FunTester/6998151

相关文章

  • 映像深刻的bug
    示例如下: 我之前测电商业务时,我们的系统在展示商品的时候每点击一个商品就会跳转到相应的详情页,url格式大概=~/productid.html,如果知道productid的话可以直接修改url跳转到其商品的详情页。类似的情况是,用户下单的时候会生成一个ordid,生成的订单详情页的url=~/ordidhtml,于是我......
  • 無法將檔案 "obj\Debug\xxx.exe" 複製到 "bin\Debug\xxx.exe"。由於另一個處理序
     解决方案:暂时不清楚为什么关闭debug后再次发起debug会出现这种情况,但是这种方法可解决上述错误 ......
  • 5个高并发场景优化的衡量指标
    本文分享自华为云社区《【高并发】性能优化有哪些衡量指标?需要注意什么?》,作者:冰河。面试官:平时工作中有没有做过一些性能优化相关的工作呢?首先,我们来分析下面试官的这个问题。其实,以我本人招聘面试的经验来说,如果面试官问出了这样的一个问题。本质上不只是想让面试者简单的回......
  • 一次关于mybatis的bug解决
    bug表现: 网上的一些映射的对应关系我这边都排除了,实际的是创建级联文件夹时候要逐个去创建:比如com.xx.xx.xx,不能一次性创建,不然idea会把这个识别成一个文件夹,而不是级联的文件夹,编译的时候,mapper类和对应的mapper.xml就不会编译到一起,会报找不到的错误,判断方式:右键该文件夹—......
  • 高并发系统架构
    可以分为以下6点:系统拆分缓存MQ分库分表读写分离ElasticSearch系统1.系统拆分将一个系统拆分为多个子系统,用dubbo来搞。然后每个系统连一个数据库,这样本来就一个库,现在多个数据库,不也可以扛高并发么。2.缓存大部分的高并发场景,都是读多写少,那你完全可以在数据库和......
  • java多线程并发面试题总结(史上最全40道)
    1、多线程有什么用?一个可能在很多人看来很扯淡的一个问题:我会用多线程就好了,还管它有什么用?在我看来,这个回答更扯淡。所谓"知其然知其所以然","会用"只是"知其然","为什么用"才是"知其所以然",只有达到"知其然知其所以然"的程度才可以说是把一个知识点运用自如。OK,下面说说我对这个......
  • java多线程并发面试题总结(史上最全40道)
    1、多线程有什么用?一个可能在很多人看来很扯淡的一个问题:我会用多线程就好了,还管它有什么用?在我看来,这个回答更扯淡。所谓"知其然知其所以然","会用"只是"知其然","为什么用"才是"知其所以然",只有达到"知其然知其所以然"的程度才可以说是把一个知识点运用自如。OK,下面说说我对这个问......
  • 【高并发】SimpleDateFormat类到底为啥不是线程安全的?(附六种解决方案,建议收藏)
    大家好,我是冰河~~首先问下大家:你使用的SimpleDateFormat类还安全吗?为什么说SimpleDateFormat类不是线程安全的?带着问题从本文中寻求答案。提起SimpleDateFormat类,想必做过Java开发的童鞋都不会感到陌生。没错,它就是Java中提供的日期时间的转化类。这里,为什么说SimpleDateFormat类有......
  • java--并发容器 ConcurrentMap
    在JDK1.4以下只有Vector和Hashtable是线程安全的集合(也称并发容器,Collections.synchronized*系列也可以看作是线程安全的实现)。从JDK5开始增加了线程安全的Map接口ConcurrentMap和线程安全的队列BlockingQueue(尽管Queue也是同时期引入的新的集合,但是规范并没有规定一定是线程安全......
  • Scala并发
     Runnable/CallableRunnable只有一个没有返回值的方法traitRunnable{defrun():Unit}Callable的方法和run类似,只不过它有一个返回值traitCallable[V]{defcall():V}线程Scala的并发是建立在Java的并发模型上的。在Sun的JVM上,对于一个IO密集型的任务,我们可以在单机......