案例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方法将对象进行清除。

现状:

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

总结

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

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


相关文章
|
2月前
|
Python
Python学习之路 02 之分支结构
Python学习之路 02 之分支结构
457 0
Python学习之路 02 之分支结构
|
2月前
|
Java Python 开发者
Python 学习之路 01基础入门---【Python安装,Python程序基本组成】
线程池详解与异步任务编排使用案例-xian-cheng-chi-xiang-jie-yu-yi-bu-ren-wu-bian-pai-shi-yong-an-li
473 2
Python 学习之路 01基础入门---【Python安装,Python程序基本组成】
|
2月前
|
Java
线程池详解与异步任务编排使用案例-xian-cheng-chi-xiang-jie-yu-yi-bu-ren-wu-bian-pai-shi-yong-an-li
线程池详解与异步任务编排使用案例-xian-cheng-chi-xiang-jie-yu-yi-bu-ren-wu-bian-pai-shi-yong-an-li
49 0
|
2天前
|
Java
线程安全问题-卖票案例实现
线程安全问题-卖票案例实现
6 0
|
3天前
|
存储 程序员
LabVIEW性能和内存管理 7 LabVIEW中局部和全局变量的内存分配
LabVIEW性能和内存管理 7 LabVIEW中局部和全局变量的内存分配
|
4天前
|
设计模式 消息中间件 安全
【Java多线程】关于多线程的一些案例 —— 单例模式中的饿汉模式和懒汉模式以及阻塞队列
【Java多线程】关于多线程的一些案例 —— 单例模式中的饿汉模式和懒汉模式以及阻塞队列
9 0
|
18天前
|
SQL Dubbo Java
案例分析|线程池相关故障梳理&总结
本文作者梳理和分享了线程池类的故障,分别从故障视角和技术视角两个角度来分析总结,故障视角可以看到现象和教训,而技术视角可以透过现象看到本质更进一步可以看看如何避免。
83779 17
案例分析|线程池相关故障梳理&总结
|
19天前
|
安全 Java 调度
HashMap很美好,但线程不安全怎么办?ConcurrentHashMap告诉你答案!
HashMap很美好,但线程不安全怎么办?ConcurrentHashMap告诉你答案!
37 1
|
26天前
|
存储 安全 Java
Java中的容器,线程安全和线程不安全
Java中的容器,线程安全和线程不安全
18 1
|
1月前
|
Java Shell
java中jvm使用jststak定位线程cpu占用内存高的线程
java中jvm使用jststak定位线程cpu占用内存高的线程
15 5