简体   繁体   English

Java 如何避免在循环中使用 Thread.sleep()

[英]Java how to avoid using Thread.sleep() in a loop

From my main I am starting two threads called producer and consumer.从我的主要开始,我开始了两个名为生产者和消费者的线程。 Both contains while(true) loop.两者都包含while(true)循环。 Producer loop is UDP Server hence it does not require sleep.生产者循环是 UDP 服务器,因此它不需要睡眠。 My problem is in the Consumer loop.我的问题出在消费者循环中。 Consumer loop remove the objects from the linked queue and pass it on to a function for further processing.消费者循环从链接队​​列中删除对象并将其传递给函数以进行进一步处理。 From what researched it is not a good practice to use thread sleep in a loop as at times O/S will not release at end of set time.根据研究,在循环中使用线程睡眠不是一个好习惯,因为有时 O/S 不会在设置时间结束时释放。 If I remove thread sleep when the application is ideal it drags CPU to 20 to 30%.如果我在应用程序理想时删除线程睡眠,它会将 CPU 拖到 20% 到 30%。

class Producer implements Runnable {
    private DatagramSocket dsocket;
    FError fer = new FError();

    int port =1548;
    ConcurrentLinkedQueue<String> queue;

    Producer(ConcurrentLinkedQueue<String> queue){
        this.queue = queue; 
    }

    @Override
    public void run() {

        try {

            // Create a socket to listen on the port.
            dsocket = new DatagramSocket(port);
            // Create a buffer to read datagrams into.
            byte[] buffer = new byte[30000];
            // Create a packet to receive data into the buffer
            DatagramPacket packet = new DatagramPacket(buffer,
            buffer.length);

            while (true) {
                try {

                   // Wait to receive a datagram
                    dsocket.receive(packet);
                    //Convert the contents to a string,
                    String msg = new String(buffer, 0, packet.getLength());

                    int ltr = msg.length();
                     // System.out.println("MSG =" + msg);

                    if(ltr>4)
                    {

                        SimpleDateFormat sdfDate = new SimpleDateFormat  ("yyyy-MM-dd HH:mm:ss");//dd/MM/yyyy

                        Date now = new Date();
                        String strDate = sdfDate.format(now);

                        //System.out.println(strDate);

                        queue.add(msg + "&" + strDate);

                     // System.out.println("MSG =" + msg);
                    }

                  // Reset the length of the packet before reusing it.
                   packet.setLength(buffer.length);

                } catch (IOException e) {
                    fer.felog("svr class", "producer", "producer thread",e.getClass().getName() + ": " + e.getMessage());
                    dsocket.close();
                    break; 
                }
            }

        } catch (SocketException e) {
          fer.felog("svr class", "producer","Another App using the udp port " + port, e.getClass().getName() + ": " + e.getMessage()); 

        }

    }

}

class Consumer implements Runnable {

    String str;  
    ConcurrentLinkedQueue<String> queue;

    Consumer(ConcurrentLinkedQueue<String> queue) {
        this.queue = queue;  
    }

    @Override
    public void run() {

        while (true) {
            try {

                while ((str = queue.poll()) != null) {

                    call(str);  // do further processing

                   }
            } catch (IOException e) {
                ferpt.felog("svr class", "consumer", "consumer thread", e.getClass().getName() + ": " + e.getMessage());
                break;
            }

            try {
                Thread.sleep(500);
            } catch (InterruptedException ex) {

                ferpt.felog("svr class", "consumer","sleep", ex.getClass().getName() + ": " + ex.getMessage());
            }

        }

    }

}

Instead of making Consumer extend Runnable you could change your code to incorporate a ScheduledExecutorService which runs the polling of the queue every half a second instead of making the thread sleep.您可以更改代码以合并ScheduledExecutorService ,而不是让 Consumer extend Runnable ,该服务每半秒运行一次队列轮询,而不是让线程休眠。 An example of this would be这方面的一个例子是

public void schedule() {
    ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
    executor.scheduleAtFixedRate(() -> {
        String str;
        try {
            while ((str = queue.poll()) != null) {
                call(str);  // do further processing
            }
        } catch (IOException e) {
            ferpt.felog("svr class", "consumer", "consumer thread", e.getClass().getName() + ": " + e.getMessage());
        }
    }, 0, 500, TimeUnit.MILLISECONDS);
}

The proper solution to your problem is to use a blocking queue.解决问题的正确方法是使用阻塞队列。 It gives you several advantages:它为您提供了几个优势:

  • does not waste cpu busy waiting不浪费cpu忙等待
  • can have limited capacity - imagine you have a fast producer, but a slow consumer -> if the queue is not limited in size, then your application can easily reach OutOfMemory condition容量可能有限 - 假设您有一个快速的生产者,但一个缓慢的消费者 -> 如果队列的大小没有限制,那么您的应用程序很容易达到 OutOfMemory 条件

Here is a small demo, which you can play with:这是一个小演示,您可以使用它:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class ProdConsTest {
    public static void main(String[] args) throws InterruptedException {
        final BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
        final Runnable producer = () -> {
            for (int i = 0; i < 1000; i++) {
                try {
                    System.out.println("Producing: " + i);
                    queue.put(i);

                    //Adjust production speed by modifying the sleep time
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    //someone signaled us to terminate
                    break;
                }
            }
        };

        final Runnable consumer = () -> {
            while (true) {
                final Integer integer;
                try {
                    //Uncomment to simulate slow consumer:
                    //Thread.sleep(1000);

                    integer = queue.take();
                } catch (InterruptedException e) {
                    //someone signaled us to terminate
                    break;
                }
                System.out.println("Consumed: " + integer);
            }
        };


        final Thread consumerThread = new Thread(consumer);
        consumerThread.start();

        final Thread producerThread = new Thread(producer);
        producerThread.start();

        producerThread.join();
        consumerThread.interrupt();
        consumerThread.join();
    }
}

Now uncomment the sleep() in the consumer and observe what happems with the application.现在取消消费者中的sleep()注释并观察应用程序发生了什么。 If you were using a timer based solution such as the proposed ScheduledExecutorService or you were busy waiting, then with fast producer, the queue would grow uncontrollably and eventually crash your application如果您正在使用基于计时器的解决方案,例如建议的ScheduledExecutorService或者您正忙于等待,那么使用快速生产者,队列将无法控制地增长并最终使您的应用程序崩溃

Let the consumer wait() on an object that both have access to, and let the producer notify() listeners on this object when there's new messages.让消费者wait()在一个都可以访问的对象上,让生产者notify()在有新消息时监听这个对象。 The consumer should remove all messages then, not just a single one like in the example.消费者应该删除所有消息,而不是像示例中那样只删除一条消息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM