如何避免使用Thread.Sleep

9
我以前从未深入思考过Thread.Sleep的使用,直到我下载了最新版本的Netbeans。现在Netbeans警告你不要使用Thread.Sleep。因此,我对这个话题进行了一些研究,并发现有人声称,只有在调试/测试目的时才需要使用Thread.Sleep,如果你在其他任何时间使用它,就是编写不良代码。
那么我的问题是,在以下情况下如何避免使用Thread.Sleep。
我编写了一个与另一个应用程序进行交互的服务器应用程序。该服务器有两个线程:
1. 处理通过套接字传输的数据并发送其他信息或纯粹的确认。 2. 这是主线程。启动套接字线程后,进入无限循环。在此while循环中,我检查套接字线程是否仍处于活动状态,并且用户是否通过TrayIcon界面请求退出应用程序。然后我睡眠并继续这个while循环。
对于这个应用程序,TrayIcon是唯一的UI。
以下是我参考的代码片段:
// continues running as long as the exitth file is not present and 
// the tray icon is not in a safe to exit status.

while(doNotExit())
{

    if (getPrimaryThread() == null || !getPrimaryThread().isAlive())
        resetsThreadAndSocket();

    try
    {
        // check to see if the socket threads are still active, if not create new ones.
        if ((getPrimaryThread() == null || !getPrimaryThread().isAlive()))       
            createSocketThread();

        // check right before sleeping that the user does not want to exit.
        if(getTrayIcon().isExiting())
            break;

        // puts the main Thread to sleep for 3 seconds
           Thread.sleep(3000);
    }
    catch(SQLException ex)
    {
        _log.error(ex.getMessage(), ex);
        restartDatabase();
    }
}

2
你为什么需要在这里睡觉呢? - Elalfer
我们这些对多线程不熟悉的人(包括我在内)会担心过度消耗处理器资源,或者导致活锁问题。 - Pete
我不确定你在这里为什么要执行Thread.sleep()。你是想防止访问共享资源(某个数据结构或对象)吗? - pseudoramble
@Elalfer,@Doug,显然这个sleep是为了防止该应用程序烧毁整个处理器,因为该线程在其整个生命周期中从未阻塞或等待(除了sleep)。 - Tim Bender
我需要在这里睡觉的原因是因为我有一个独立的线程(createSocketThread)来处理所有的套接字任务。主线程的责任是:1)处理来自TrayIcon的响应,例如退出程序。2)检查套接字是否已关闭,并在必要时创建新的socketthread。 - Derek
如果我不睡觉,主线程在执行检查任务时会将我的计算机占用到100%。为了保持简单,我没有包含所有涉及的代码。 - Derek
4个回答

6
在大多数情况下,“首选”方法是使用JavaSE内置的ScheduledExecutorService执行定期任务,而不是每次都使用while循环和Thread.Sleep()重新实现它。您的示例本质上没有问题。自Java 5以来,该语言已经具有更强大的支持来完成这项任务。

3

不要使用Thread.sleep(3000),请使用以下方式:

getPrimaryThread().join(3000)

这将等待线程退出3秒钟。


2

您应该考虑附加一个事件监听器到您的托盘图标,而不是轮询其状态。这样,您就不需要一个额外的线程来监视。

如果由于某种原因您不能这样做,您仍然可以省去额外的线程,因为 Timer 类可以为您完成等待。


1
你似乎对某些条件(可能是RuntimeException或Error?)会导致你的socket线程崩溃感到过分担心。理想情况下,你应该设计你的Socket线程以使其能够自我保护免于崩溃。以下示例创建了一个只能通过JVM错误或线程中断来打破的循环:
public void run() {
    while(!Thread.currentThread.isInterrupted()) {
        try {
            //you application logic
        } catch (RuntimeException e) {
           //log uncaught exception
        }
    }
}

为了关闭应用程序,您需要将一个监听器附加到包含对SocketThread的引用的TrayIcon上,并通过简单地中断它来停止它。
socketThread.interrupt();

我会把如何向TrayIcon添加ActionListener的问题留给你自己去解决。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接