美文网首页程序员
Java while循环cpu占用高排查和优化

Java while循环cpu占用高排查和优化

作者: 不废的废柴 | 来源:发表于2019-01-18 18:14 被阅读5次

    Java 自带性能分析工具

    命令行工具的功能都很强大,像jmap、jstat、jstack、jps这些,功能和一些收费软件差不多,但是没有GUI看起来就有些费劲。由于使用Windows分析就使用自带的Jmc来用了,只要在命令行输入jmc就可以启动。


    jmc示例图

    cpu占用排查

    由于已经知道cpu占用原因,这里就直接给出错误的示例。如果在服务器上排查需要结合上述命令行工具,具体使用可以参考这里

    • 错误示例
    public class TestCpu {
        public static void main(String[] args) throws InterruptedException {
            TaskThread thread = new TaskThread();
            thread.setName("Test Cpu");
            thread.start();
    
            // 为了让程序不退出,这里休眠一个较长时间
            // sleep 在执行后基本不产生性能损耗
            Thread.sleep(1000000);
        }
    }
    
    class TaskThread extends Thread {
    
        // 由于while循环占用锁导致变量无法观测到变化,可以使用volatile解决,也可以使用并发包下的集合解决
        private volatile Queue<Runnable> tasks = new ArrayDeque<>();
    
        public synchronized void add(Runnable runnable) {
            tasks.add(runnable);
        }
    
        @Override
        public void run() {
            while (true) {
                while (!tasks.isEmpty()) {
                    Runnable runnable = tasks.remove();
                    runnable.run();
                }
            }
        }
    }
    
    
    

    上述代码在运行又占用cpu稳定在15%左右,机器配置为I7 4核8G

    • 使用jcm分析性能消耗
      先使用飞行记录器记录一段时间的运行情况

      飞行记录器
      线程
      可以看到测试线程基本占用了所有资源(实际服务器上业务逻辑相对复杂,占有率一般不会占这么高)。
      但是代码实际只是判断了一个集合是否为空,为什么CPU占用会这么高呢?原因可以参考这里
      简单解释就是在Windows上如果一个进程得到了 CPU 时间,除非它自己放弃使用 CPU ,否则将完全霸占 CPU。在Linux上相对好一点,因为时基于时间片算法的。
      在知道原因后就比较好办了,解决方法无非就是在不需要执行无谓的循环的时把CPU释放出来。最简单的做法就是在while循环种调用Thread.sleep方法释放CPU的占用。但这样存在频繁的切换线程调度的问题。
    • 使用wait和notify阻塞和唤醒线程
      wait和notify的原理可以参考这里。对TaskThread修改代码如下,配合这2个方法可以保证在线程空闲时处于一个挂起状态,CPU占用在空闲时也基本为0了。

    class TaskThread extends Thread {
    
        private final Deque<Runnable> tasks = new ArrayDeque<>();
    
        public void add(Runnable runnable) {
            synchronized (tasks) {
                tasks.add(runnable);
                if (tasks.pollFirst() == runnable) {
                    tasks.notify();
                }
            }
        }
    
        @Override
        public void run() {
            try {
                while (true) {
                    synchronized (tasks) {
                        if (tasks.isEmpty()) {
                            tasks.wait();
                        }
                    }
                    while (!tasks.isEmpty()) {
                        Runnable runnable = tasks.remove();
                        runnable.run();
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    

    相关文章

      网友评论

        本文标题:Java while循环cpu占用高排查和优化

        本文链接:https://www.haomeiwen.com/subject/wqfodqtx.html