项目实战典型案例15——高并发环境下由于使用全局变量导致数据混乱 高并发环境下对象被大量创建,导致GC并是CPU飙升

简介: 项目实战典型案例15——高并发环境下由于使用全局变量导致数据混乱 高并发环境下对象被大量创建,导致GC并是CPU飙升

高并发环境下由于使用全局变量导致数据混乱 高并发环境下对象被大量创建,导致GC并是CPU飙升


一:背景介绍

  1. 出现域名不全的情况,这里与全局变量有关。
  2. 每一次登陆都会重新创建一个对象,放到公共变量中。如果遇到高并发,这里的对象将会被大量的创建,然后上一个对象会失去引用,等待垃圾会后器进行回收,频繁的GC将会导致CPU飙升。

二:思路&方案

针对于问题一,在并发环境下有偶遇使用全局变量导致数据混乱。

方案一:将全局变量修改为局部变量。

方案二:使用ThreadLocal,该线程变量对于其他线程而言是隔离的,该变量是当前线程独有的变量,那就不存在线程共享变量的问题。

方案三:对使用到全局变量的方法或者类使用synchronized并且最后需要对全局变量进行还原。

下面对方案二和方案三进行实践,在进行实践之前我们模拟一下并发条件下使用全局变量出现数据混乱的问题。

数据混乱现象复现

业务流程:启动100个线程调用两个方法进行字符串拼接,然后数据拼接的结果。

计算类

//声明一个全局变量,ArrayList线程不安全
    List<String> keyList=new ArrayList<>();
    public  void count2() throws InterruptedException {
        keyList = new ArrayList<>();
        keyList.add("a");
        keyList.add("b");
        keyList.add("c");
        keyList.add("d");
        System.out.println("ARPRO"+keyList);
       }

客户端类

public class Client {
    public static void main(String[] args) {
        // 定义线程实现接口
        Runnable runnable = new Runnable(){
            Counter  counter = new Counter();
            @Override
            public void run() {
                try {
                    counter.count2();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        };
        // 启动100个线程
        for( int i= 0;i< 100;i++) {
            new Thread(runnable).start();
        }
    }
}

实现结果

出现数据混乱的现象

使用ThreadLocal

    private static ThreadLocal<List<String>> keyList = new ThreadLocal<List<String>>(){
        //对keyList进行初始化
        @Override public List<String> initialValue() {
            return new ArrayList<String>();
        }
    };
    public  void count2() throws InterruptedException {
        keyList.get().add("a");
        keyList.get().add("b");
        keyList.get().add("c");
        keyList.get().add("d");
        System.out.println("ARPRO"+keyList.get());
       }

实现结果

没有出现数混乱的情况

使用synchronized进行优化

synchronized 保证方法某一时刻只有一个线程去执行,从而保证线程安全性;

synchronized 可以修饰方法,对象实例,某个类,代码块

//声明一个全局变量,ArrayList线程不安全
    List<String> keyList=new ArrayList<>();
    public synchronized void count2() throws InterruptedException {
        keyList.add("a");
        keyList.add("b");
        keyList.add("c");
        keyList.add("d");
        System.out.println("ARPRO"+keyList);
        //由于虽然使用synchronized锁住了count2()这个方法保证同一时刻只有一个线程执行
        //但是线程共享全局变量,所以当方法执行完成之后,需要将keyList的值进行还原
        keyList.removeAll(keyList);
       }

ThreadLocal与synchronized的区别

1、Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。

2、Synchronized是利用锁的机制,代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的实例副本。

针对于问题二 每一次登陆都会重新创建一个对象,放到公共变量中。如果遇到高并发,这里的对象将会被大量的创建,然后上一个对象会失去引用,等待垃圾会后器进行回收,频繁的GC将会导致CPU飙升。

1.我们实例化一个对象,会将对象存储在堆中,会将这个对象的引用存储在栈中。当我们再次实例化keyList这个对象的时候,会再次创建这个对象,并重新在栈中创建这个对象的引用。

2.为什么频繁GC会导致cpu使用率过高,一定时间内分享cup时间片的线程数量是有限的,其中做“非业务工作”的线程占用的时间片越多,cpu使用率越高。

3.频繁GC会增加"非业务工作”的线程,这些线程会占用一定数量的cpu时间分片,导致cpu空闲时间减少,cpu使用率升高。

优化代码

结合问题一的优化,可以解决这个问题。

例如:

//声明一个全局变量,ArrayList线程不安全
    List<String> keyList=new ArrayList<>();
    public synchronized void count2() throws InterruptedException {
        keyList.add("a");
        keyList.add("b");
        keyList.add("c");
        keyList.add("d");
        System.out.println("ARPRO"+keyList);
        //由于虽然使用synchronized锁住了count2()这个方法保证同一时刻只有一个线程执行
        //但是线程共享全局变量,所以当方法执行完成之后,需要将keyList的值进行还原
        keyList.removeAll(keyList);
       }

四:总结

1.在项目开发过程中,对于公共变量的使用一方面需要慎重,需要考虑是否有并发,多线程的情况,然后根据实际情况选择对应的处理措施。

2.实例化类,也需要慎重,实例化类是有有必要。


目录
相关文章
|
2月前
|
存储 监控 固态存储
在高并发环境下,如何优化 WAL 的写入性能?
在高并发环境下,如何优化 WAL 的写入性能?
|
2月前
|
消息中间件 负载均衡 应用服务中间件
高并发环境下的Nginx整合方案
【8月更文挑战第20天】在高并发环境下,整合Nginx代理服务器、静态文件服务器、Tomcat集群、Mycat数据库读写分离和消息队列,可以构建一个强大、灵活且可扩展的Web服务架构。
32 1
|
2月前
|
安全 Java Go
探索Go语言在高并发环境中的优势
在当今的技术环境中,高并发处理能力成为评估编程语言性能的关键因素之一。Go语言(Golang),作为Google开发的一种编程语言,以其独特的并发处理模型和高效的性能赢得了广泛关注。本文将深入探讨Go语言在高并发环境中的优势,尤其是其goroutine和channel机制如何简化并发编程,提升系统的响应速度和稳定性。通过具体的案例分析和性能对比,本文揭示了Go语言在实际应用中的高效性,并为开发者在选择合适技术栈时提供参考。
|
2月前
|
存储 缓存 运维
优化高并发环境下的数据库查询性能:实战经验与技巧
在高并发环境下,数据库性能往往成为系统瓶颈。本文将深入探讨在高并发场景下优化数据库查询性能的策略与实践,包括索引优化、查询优化、数据库架构设计以及缓存机制的应用。通过对具体案例的分析,读者将能够掌握提升数据库性能的关键技术,从而在面对大规模用户请求时提高系统的响应速度和稳定性。
|
2月前
|
存储 缓存 数据处理
计算机临时存储CPU运算数据
【8月更文挑战第4天】
48 8
|
3月前
|
并行计算 API 数据处理
GPU(图形处理单元)因其强大的并行计算能力而备受关注。与传统的CPU相比,GPU在处理大规模数据密集型任务时具有显著的优势。
GPU(图形处理单元)因其强大的并行计算能力而备受关注。与传统的CPU相比,GPU在处理大规模数据密集型任务时具有显著的优势。
|
3月前
|
Java
手把手教你java CPU飙升300%如何优化
手把手教你java CPU飙升300%如何优化
49 0
|
3月前
|
监控 Java 中间件
FGC频繁导致CPU 飙升定位及JVM配置优化总结
FGC频繁导致CPU 飙升定位及JVM配置优化总结
76 0
|
3月前
|
设计模式 安全 Java
Java面试题:如何实现一个线程安全的单例模式,并确保其在高并发环境下的内存管理效率?如何使用CyclicBarrier来实现一个多阶段的数据处理任务,确保所有阶段的数据一致性?
Java面试题:如何实现一个线程安全的单例模式,并确保其在高并发环境下的内存管理效率?如何使用CyclicBarrier来实现一个多阶段的数据处理任务,确保所有阶段的数据一致性?
46 0
|
3月前
|
存储 安全 Java
Java面试题:假设你正在开发一个Java后端服务,该服务需要处理高并发的用户请求,并且对内存使用效率有严格的要求,在多线程环境下,如何确保共享资源的线程安全?
Java面试题:假设你正在开发一个Java后端服务,该服务需要处理高并发的用户请求,并且对内存使用效率有严格的要求,在多线程环境下,如何确保共享资源的线程安全?
54 0
下一篇
无影云桌面