• java用while循环设计轮询线程的性能问题


    java用while循环设计轮询线程的性能问题

           轮询线程在开发过程中的应用是比较广泛的,在这我模拟一个场景,有一个队列和轮询线程,主线程往队列中入队消息,轮询线程循环从队列中读取消息并打印消息内容。有点类似Android中Handler发送消息。

    首先定义一个Message类。

    public class Message {
        private String content;
        public Message(String content)
        {
            this.content=content;
        }
        public void display(){
            System.out.println(content);
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

           这个类很简单,在构造的时候传入消息内容,display()方法输出打印消息内容。

           接下来定义一个轮询线程,一开始蠢萌的我这么写

    public class PollingThread extends Thread implements Runnable {
        public static Queue<Message> queue = new LinkedTransferQueue<Message>();
    
        @Override
        public void run() {
            while (true) {
                while (!queue.isEmpty()) {
                    queue.poll().display();
                }
            }
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

           这个轮询线程功能很简单,一直不停的轮询队列,一旦队列中有消息进入,就将它出队并调用display()方法输出内容。

           接下来在Main()方法中循环创建消息将它放入队列

    public class Main {
        public static void main(String[] args){
            PollingThread pollingThread=new PollingThread();
            pollingThread.start();
            int i=1;
            while(true)
            {
                PollingThread.queue.offer(new Message("新消息"+i));
                i++;
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    运行结果

    新消息1
    新消息2
    新消息3
    新消息4
    新消息5
    新消息6
    新消息7
    新消息8
    新消息9
    新消息10
    新消息11
    新消息12
    新消息13
    新消息14
    新消息15
    ......
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

           虽然这么做,功能是实现了,可是我们来看下cpu占用率

    这里写图片描述

           程序刚编译启动的时候cpu占用率到了100%,开始运行后一直处于44%左右。这个占用率还是比较高的,那么我们来分析一下这个轮询线程,假设一直没有消息入队,或者消息入队的间隔时间比较长的话,它就会循环的去执行while(!queue.isEmpty())判断队列是否为空,其实这个判断操作是非常耗性能的。我们应该把这个轮询线程设计的更合理一点。那么怎样设计比较合理呢?既然循环对队列判空是比较浪费性能的操作,那么我们如果可以让这个轮询线一开始处于阻塞状态,主线程在每次入队消息的时候通知轮询线程循环出队输出消息内容,当队列为空的时候轮询线程又自动的进入阻塞状态,就可以避免轮询线程死循环对队列判空。接下来我们改造一下轮询线程和主线程的代码

    public class PollingThread extends Thread implements Runnable {
        public static Queue<Message> queue = new LinkedTransferQueue<Message>();
    
        @Override
        public void run() {
            while (true) {
                while (!queue.isEmpty()) {
                    queue.poll().display();
                }
                //把队列中的消息全部打印完之后让线程阻塞
                synchronized (Lock.class)
                {
                    try {
                        Lock.class.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    public class Main {
        public static void main(String[] args){
            PollingThread pollingThread=new PollingThread();
            pollingThread.start();
            int i=1;
            while(true)
            {
                PollingThread.queue.offer(new Message("新消息"+i));
                i++;
                //有消息入队后激活轮询线程
                synchronized (Lock.class)
                {
                    Lock.class.notify();
                }
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

           这时候再来运行一下

    运行结果

    新消息1
    新消息2
    新消息3
    新消息4
    新消息5
    新消息6
    新消息7
    新消息8
    新消息9
    新消息10
    新消息11
    ......
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    再来看看cpu占用率

    这里写图片描述

           轮询线程经过改造后cpu占用率基本稳定在11%,比之前44%下降了不少。

           所以,在编写轮询线程的时候,尽量用通知的方式去让轮询线程执行操作,避免重复的条件判断造成的性能浪费。

    http://blog.csdn.net/q15858187033/article/details/60583631

  • 相关阅读:
    10年学到的编程经验总结
    测试框架 Mocha 实例教程
    在 Flutter 中玩转 Objective-C Block
    使用Gulp压缩混淆JS的相关配置
    Gulp前端自动化构建工具
    常用的十大 NodeJS 框架
    如何使用Vue中的嵌套插槽(包括作用域插槽)
    通过Python代码操作MySQL:
    使用python发邮件:
    jquery:
  • 原文地址:https://www.cnblogs.com/jiahuafu/p/6855568.html
Copyright © 2020-2023  润新知