案例15-ArrayList线程不安全,共用全局变量导致数据错乱问题,占用内存情况

简介: 案例15-ArrayList线程不安全,共用全局变量导致数据错乱问题,占用内存情况

背景

存入redis的值,可能会出现错误的情况。如果出现错误,接口将会报错。

多个方法一起修改一个公共变量的值,造成数据混乱,导致存入redis中的key值错误

还有每次登陆都会重现创建一个对象,放到公共变量中,遇到并发,对象会被大量地创建,

上一个对象会失去引用,等待垃圾回收器进行回收,导致CPU飙升。

上边公共变量的字符串拼接出现问题,导致下边这张图中的域名中的字符串出现问题。

由上图可知:

**1、使用了线程不安全的ArrayList作为公共变量

2、每次给Arraylist重新赋值的时候都创建了一个新的对象,堆积了大量要回收的旧对象,导致CPU飙升**

(GC会消耗大量CPU和内存来实现垃圾回收)

思路&方案

复现问题:

测试类:

public class ThreadTest {
    //新建一个list作为成员变量
    List<String> testList ;
    public void updateTestList(){
        testList = new ArrayList<>();
        testList.add("a01+");
        testList.add("a02+");
        testList.add("a03+");
        testList.add("a04+");
        //打印一下看看有什么
        System.out.println("updateTestList"+testList);
    }
    public void updateTestList2(){
        testList = new ArrayList<>();
        testList.add("b01+");
        testList.add("b02+");
        testList.add("b03+");
        testList.add("b04+");
        //看一下list里有什么
        System.out.println("updateTestList2"+testList);
    }
}

客户端:

public class Main {
    public static void main(String[] args) {
        ThreadTest threadTest = new ThreadTest();
        //开一个多线程测试一下
        for (int i = 0; i < 100; i++) {
          Thread thread = new Thread(new Runnable() {
              @Override
              public void run() {
                  threadTest.updateTestList();
                  threadTest.updateTestList2();
              }
          });
        thread.start();
        }
    }
}

正常结果只会出现下面两种情况

updateTestList[a01+, a02+, a03+, a04+]

updateTestList2[b01+, b02+, b03+, b04+]

实际上:

注重变量的作用域和生命周期,还要考虑并发量高的时候考虑线程安全,并发的时候还要将对象进行置空。

第一个问题解决方案:

1、在方法之前加 synchronized 关键字。

2、使用ThreadLocal变量。
public class ThreadTest2 {
    ThreadLocal<List<String>> testList = ThreadLocal.withInitial(()->new ArrayList<>());
    public  void updateTestList(){
        testList.get().removeAll(testList.get());
        testList.get().add("a01+");
        testList.get().add("a02+");
        testList.get().add("a03+");
        testList.get().add("a04+");
        //打印一下看看有什么
        System.out.println("updateTestList"+testList.get());
    }
    public  void updateTestList2(){
        testList.get().removeAll(testList.get());
        testList.get().add("b01+");
        testList.get().add("b02+");
        testList.get().add("b03+");
        testList.get().add("b04+");
        //看一下list里有什么
        System.out.println("updateTestList2"+testList.get());
    }
}

结果:

第二个问题(对象重复创建导致CPU和内存飙升)解决方案:

1、使用List的RemoveAll方法将对象进行清除。

现状:

这样就不会持续开辟内存空间。

总结

考虑成本,凡事都要考虑成本。

我们要有无限思维,当只有一个对象的时候我们写的代码不会出现上述问题,但是对象一多就会出现数据错乱的问题,内存飙升的问题,我们的系统不会只有一个用户,所以无限思维是我们必须要考虑的一件事情,考虑并发,考虑将来。而不是只顾眼前。


相关文章
|
10月前
|
存储 监控 Java
JAVA线程池有哪些队列? 以及它们的适用场景案例
不同的线程池队列有着各自的特点和适用场景,在实际使用线程池时,需要根据具体的业务需求、系统资源状况以及对任务执行顺序、响应时间等方面的要求,合理选择相应的队列来构建线程池,以实现高效的任务处理。
495 12
|
11月前
|
存储 缓存 监控
Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
本文介绍了Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
1068 7
|
11月前
|
缓存 安全 Java
【JavaEE】——单例模式引起的多线程安全问题:“饿汉/懒汉”模式,及解决思路和方法(面试高频)
单例模式下,“饿汉模式”,“懒汉模式”,单例模式下引起的线程安全问题,解锁思路和解决方法
|
11月前
|
Java 调度
【JavaEE】——线程的安全问题和解决方式
【JavaEE】——线程的安全问题和解决方式。为什么多线程运行会有安全问题,解决线程安全问题的思路,synchronized关键字的运用,加锁机制,“锁竞争”,几个变式
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
177 1
|
NoSQL 程序员 Linux
轻踩一下就崩溃吗——踩内存案例分析
踩内存问题分析成本较高,尤其是低概率问题困难更大。本文详细分析并还原了两个由于动态库全局符号介入机制(it's a feature, not a bug)触发的踩内存案例。
【Java集合类面试十二】、HashMap为什么线程不安全?
HashMap在并发环境下执行put操作可能导致循环链表的形成,进而引起死循环,因而它是线程不安全的。
|
安全 算法 Java
【Java集合类面试二】、 Java中的容器,线程安全和线程不安全的分别有哪些?
这篇文章讨论了Java集合类的线程安全性,列举了线程不安全的集合类(如HashSet、ArrayList、HashMap)和线程安全的集合类(如Vector、Hashtable),同时介绍了Java 5之后提供的java.util.concurrent包中的高效并发集合类,如ConcurrentHashMap和CopyOnWriteArrayList。
【Java集合类面试二】、 Java中的容器,线程安全和线程不安全的分别有哪些?
|
消息中间件 安全 Kafka
"深入实践Kafka多线程Consumer:案例分析、实现方式、优缺点及高效数据处理策略"
【8月更文挑战第10天】Apache Kafka是一款高性能的分布式流处理平台,以高吞吐量和可扩展性著称。为提升数据处理效率,常采用多线程消费Kafka数据。本文通过电商订单系统的案例,探讨了多线程Consumer的实现方法及其利弊,并提供示例代码。案例展示了如何通过并行处理加快订单数据的处理速度,确保数据正确性和顺序性的同时最大化资源利用。多线程Consumer有两种主要模式:每线程一个实例和单实例多worker线程。前者简单易行但资源消耗较大;后者虽能解耦消息获取与处理,却增加了系统复杂度。通过合理设计,多线程Consumer能够有效支持高并发数据处理需求。
460 4
|
安全 Java 调度
python3多线程实战(python3经典编程案例)
该文章提供了Python3中多线程的应用实例,展示了如何利用Python的threading模块来创建和管理线程,以实现并发执行任务。
417 0

热门文章

最新文章

下一篇
oss云网关配置