当前位置: 首页 > 知识库问答 >
问题:

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

唐晗昱
2023-03-14

从我的main开始,我将启动两个线程,称为producer和consumer。两者都包含while(true)循环。生产者循环是UDP服务器,因此不需要Hibernate。我的问题出在消费者方面。使用者循环将对象从链接队列中移除,并将其传递给一个函数进行进一步处理。根据研究,在循环中使用线程Hibernate不是一个好的实践,因为有时O/S在设定时间结束时不会释放。如果我删除线程Hibernate,当应用程序是理想的,它拖到20%到30%的CPU。

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());
            }

        }

    }

}

共有2个答案

宗乐池
2023-03-14

解决问题的正确方法是使用阻塞队列。它为您提供了几个优点:

  • 不会浪费cpu忙于等待
  • 可以有有限的容量--假设您有一个快速的生产者,但一个缓慢的使用者->如果队列的大小不受限制,那么您的应用程序可以很容易地达到OutOfMemory条件

这里有一个小演示,你可以玩一下:

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();
    }
}

现在取消对使用者中的sleep()的注释,并观察应用程序的优点。如果您使用的是基于计时器的解决方案,如建议的ScheduleDexecutorService或者您正忙于等待,那么使用fast producer,队列将无法控制地增长,并最终使应用程序崩溃

井洲
2023-03-14

您可以更改代码以合并ScheduleDexecutorService,而不是使使用者扩展为可运行的,该ScheduleDexecutorService每半秒运行一次队列轮询,而不是使线程休眠。这方面的一个例子是

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);
}
 类似资料:
  • 问题内容: 我知道python中的循环导入问题已经出现过很多次了,我已经阅读了这些讨论。在这些讨论中反复提出的意见是,循环导入表明设计不良,应重新组织代码以避免循环导入。 有人可以告诉我在这种情况下如何避免循环导入吗?:我有两个类,并且我希望每个类都有一个构造函数(方法),该构造函数接受另一个类的实例并返回该类的实例。 更具体地说,一类是可变的,一类是不变的。哈希,比较等需要不可变的类。可变类也需

  • 问题内容: 如果我有这样的列表理解: 有效地创建一个新列表,该列表中的每个元素都包含一个空字符串,我从不使用。有没有更干净的方式编写此代码,所以我不必声明未使用的变量? 问题答案: 是for循环和元组分配中被忽略成员的标准占位符名称,例如 顺便说一句,您的列表可以在没有列表理解的情况下编写(假设您要创建不可变成员(如字符串,整数等)的列表)。

  • 问题内容: 我使用hibernate方式检索兄弟列表 Hibernate是在兄弟列表中使用惰性选择配置的,在Java端这是可行的,但是问题是当我想将Brother对象序列化为JSON时。 例如,布莱恩(Bryan)可以将马克(Mark)当作兄弟,反之亦然… 我该如何解决?有什么办法可以指示对杰克逊库的最大递归次数? 我的代码,真的很简单。 问题答案: 由于 循环参考出现问题 。 由于您可以使用两个

  • 问题内容: 我有以下(也许是常见的)问题,此刻绝对使我感到困惑: 有几个生成的事件对象扩展了抽象类,我想将它们划分为Session Bean,例如 但是将来可能会有两种以上的事件类型,因此if- else将会很长,甚至可能无法读取。另外,在这种情况下,我认为这并不是真正的“最佳实践”。 我可以在类型中添加一个抽象方法,并让它们自行划分,但随后我必须在每个实体中注入特定的Session Bean。

  • 问题内容: 具有“ instanceof”操作链被认为是“代码异味”。标准答案是“使用多态性”。在这种情况下我该怎么办? 基类有许多子类。他们都不在我的控制之下。类似的情况是Java类Integer,Double,BigDecimal等。 我确实可以控制等。 我不想使用几行代码就能完成的代码。(有时,我制作了一个HashMap将映射到的实例,将映射到的实例,等等。但是今天我想要一些更简单的方法。)

  • 如果我有一个班级ProfessorDto和一个班级StudentDto,如果ProfessorDto有一个StudentDto列表,StudentDto有一个ProfessorDto类型的属性,我如何避免循环问题?我没有给出域类的代码,但假设它与Dto的代码相同。 我是Mapstruct的新手,将域bean转换为具有简单属性的Dto,如Long,String正在工作,但在我的示例中,关系OneTo