【问题标题】:NetBeans / Java / New hint: Thread.sleep called in loopNetBeans / Java / 新提示:在循环中调用 Thread.sleep
【发布时间】:2011-04-01 22:25:53
【问题描述】:

在 NetBeans 中,有一个新提示:Thread.sleep call in loop.

问题 1:循环睡眠如何/何时会成为问题?

问题 2:如果有问题,我应该怎么做?

更新:问题 3:这是一些代码。在这种情况下告诉我是否应该在循环中使用其他东西而不是 Thread.Sleep。简而言之,这是由侦听客户端 TCP 连接的服务器使用的。如果达到与客户端的最大会话数,则在此处使用睡眠。在这种情况下,我希望应用程序等到空闲会话可用。

public class SessionManager {
    private static final int DEFAULT_PORT = 7500;
    private static final int SLEEP_TIME = 200;
    private final DatabaseManager database = new DatabaseManager();
    private final ServerSocket serverSocket = new ServerSocket(DEFAULT_PORT);

public SessionManager() throws IOException, SQLException
{
}

public void listen()
{
while (true)
    if (Session.getSessionCount() < Session.getMaxSessionCount())
        try
        {
             new Thread(new Session(database, serverSocket.accept())).start();
        }
        catch (IOException ex) { ex.printStackTrace(); }
    else
        try
        {
            Thread.sleep(SLEEP_TIME);
        }
        catch (InterruptedException ex) { ex.printStackTrace(); }
}

public static void main(String[] args) throws IOException, SQLException
{
new SessionManager().listen();
}
}

【问题讨论】:

    标签: java multithreading netbeans sleep hint


    【解决方案1】:

    循环睡眠如何/何时会成为问题?
    人们有时会使用它来代替适当的同步方法(例如等待/通知)。

    如果有问题,我应该怎么做?
    取决于你在做什么。虽然我很难想象这样做是最好的方法的情况,但我想这也是可能的。

    您可以在此主题上查看Sun's concurrency tutorial

    【讨论】:

    • 人们有时也会在读取循环中将它与测试 available() 结合使用,此时他们应该只是阻塞读取而不是浪费时间和空间。
    • 一种用法是目录清理器,它删除超过某个时间段的文件。在第一次运行之后,我们知道哪个是最旧的文件,以及它会变得“太旧”的确切时间,因此我们可以在这段时间内休眠。
    • @Jesse - 一个预定的线程池执行器可能会更好。
    【解决方案2】:

    在循环中调用 sleep 通常会导致性能下降。例如:

    while (true) {
        if (stream.available() > 0) {
           // read input
        }
        sleep(MILLISECONDS);
    }
    

    如果 MILLISECONDS 太大,那么这段代码将需要很长时间才能意识到输入可用。

    如果 MILLISECONDS 太小,那么此代码将浪费大量系统资源检查尚未到达的输入。

    sleep 在循环中的其他用途通常也是有问题的。通常有更好的方法。

    如果有问题,我应该怎么做?

    发布代码,也许我们可以给你一个明智的答案。

    编辑

    IMO,解决问题的更好方法是使用ThreadPoolExecutor

    类似这样的:

    public void listen() {
        BlockingQueue queue = new SynchronousQueue();
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                1, Session.getMaxSessionCount(), 100, TimeUnit.SECONDS, queue);
        while (true) {
            try {
                queue.submit(new Session(database, serverSocket.accept()));
            } catch (IOException ex) { 
                ex.printStackTrace();
            }
        }
    }
    

    这会将执行程序配置为与您的代码当前的工作方式相匹配。您还有许多其他方法可以做到这一点;请参阅上面的 javadoc 链接。

    【讨论】:

    • java.util.Timer 和周期性 TimerTask 怎么样?
    • @Core - 这取决于你如何使用它。如果你只是用它来实现 sleep() 的等价物,你会遇到与使用 sleep() 相同的问题。
    【解决方案3】:

    正如其他人所说,这取决于使用情况。一个合法的用途是一个设计为每 10 秒做一次事情的程序(但不是那么重要以至于需要精确的时间)。我们有很多这样的“实用程序”,每隔几分钟就会导入数据和其他类似任务。这是执行这些任务的一种简单方法,我们通常会将睡眠间隔设置为非常低并使用计数器,以便程序保持响应并轻松退出。

    int count = 0;
    while (true) {
    
        try {
            // Wait for 1 second.
            Thread.sleep(1000);
        }
        catch (InterruptedException ex) {}
    
        // Check to see if the program should exit due to other conditions.
        if (shouldExit())
            break;
    
        // Is 10 seconds up yet? If not, just loop back around.
        count++;
        if (count < 10) continue;
    
        // 10 seconds is up. Reset the counter and do something important.
        count = 0;
        this.doSomething();
    }
    

    【讨论】:

    • 在大多数情况下,使用调度的线程池执行器在技术上会更好,但是当你只是在编写一些快速而肮脏的东西时,这有时就太过分了。
    • @mjaggard 可以说我目前有一个带有循环的线程,里面有一个睡眠。它正在执行内务管理任务,例如清除不再相关的旧数据。与仅仅拥有一个长期存在的、主要是空闲的线程相比,使用调度的线程池执行器会给代码增加不必要的复杂性。对于休眠的长寿命/空闲线程,上下文切换惩罚可以忽略不计。除了删除“它不是一个好的做法”警告之外,调度线程池实际上给你带来了什么?
    • 你写出每个的代码了吗?我怀疑预定的线程池执行器版本实际上可能更短,更易于阅读并且效率非常相似。
    【解决方案4】:

    我想我在循环中遇到了sleep() 方法的一种完全合法的用法。

    我们在服务器和客户端之间有单向连接。因此,当客户端想要实现与服务器的异步通信时,他会向服务器发送消息,然后定期轮询服务器的一些响应。需要有一些超时间隔。

    Response resp = null;
    for (int i = 0; i < POLL_REPEAT && resp == null; i++) {
        try {
           Thread.sleep(POLL_INTERVAL);
        } catch (InterruptedException ie) {
        }
        resp = server.getResponse(workflowId);
    }
    

    POLL_REPEAT * POLL_INTERVAL ~ 超时间隔

    【讨论】:

    • 如果您的 API 可能的话,一个更好的解决方案是使用阻塞 IO 方法,该方法一直等到有新数据可用。
    猜你喜欢
    • 2023-03-07
    • 2015-01-12
    • 2019-06-20
    • 2012-02-06
    • 1970-01-01
    • 2012-11-19
    • 2020-10-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多