【问题标题】:Multithreading and sleeping for responsive UI in JavaJava 中响应式 UI 的多线程和休眠
【发布时间】:2013-11-10 18:21:03
【问题描述】:

我正在阅读关于多线程的这篇文章:http://www.ntu.edu.sg/home/ehchua/programming/java/j5e_multithreading.html

它建议(不要求)在单核 CPU 的 CPU 密集型任务中添加 Thread.sleep(10),并且通过这样做有效地为 CPU 提供了额外的时间来更新所有它拥有的其他线程(最重要的是,我正在尝试改进的程序的 UI)。

这对我来说没有任何意义。虽然我的程序没有响应,但我的计算机的其余部分运行良好,我相信是 Windows 为每个程序分配了 CPU 时间。

无论如何,这是个好建议吗?如果是这样,我是说“这是其他线程的 10 毫秒奖励”,还是 Thread.sleep(0) 可以正常工作?

【问题讨论】:

  • 听起来像是一些 Java 实现使用绿色线程(模拟线程)而不是真实线程时产生的建议
  • @Mark 不会 Thread.yield() 更合适吗?
  • @haraldK 可能,老实说,我以前从未见过使用 sleep 进行响应的建议,老实说,我也从未真正需要使用 yield

标签: java multithreading


【解决方案1】:

我认为对于可追溯到 2004 年的一篇文章(因为它引用了 JDK5 的新特性)以及 2006 年出现了第一批双核处理器,这是一个很好的建议。当您拥有单核并长时间运行进程时,线程屈服将使您的系统响应更快。

我认为,如果您的应用加载所有可用内核,那么今天可以应用相同的技术。对于一篇文章中的示例,我认为这个长时间运行的操作可以直接放到线程池中而不会产生任何结果。

更新

线程屈服非常依赖环境:yield(), sleep(0), wait(0,1) and parkNanos(1)

如果你想等待一小段时间,你不能假设所有这些方法都做同样的事情,平台之间也不一样。

【讨论】:

  • yield() 一点也不信任。
  • 这是我第一次听说yield() 没有按预期工作,这是事实还是怀疑?
  • @WayneUroda 根据 API,yield() 是“对调度程序的提示,表明当前线程愿意放弃其当前对处理器的使用。调度程序可以随意忽略此提示。”所以,它不能保证其他线程的执行,但这不是我想要的。
  • @AndreyChaschev “我认为如果您的应用程序加载所有可用内核,今天可以应用相同的技术。”在我的电脑上,我使用了大约 25% 的 CPU……假设这个产品没有商业化,我可以安全地假设它不会加载所有可用的内核吗?
  • @MrTi 当然。很难想象一个桌面用户应用程序会故意加载 4 个内核。如果是对单用户机器(不是集群)进行一些研究,我建议最多加载 3 个核心以避免麻烦。
【解决方案2】:

在 Windows 上的睡眠会导致主线程暂停并处理后台线程时,这显然是一个古老的建议。您可以将旧线程概念想象为队列,并且只有前端线程获得处理时间。一旦该线程被搁置(在 Windows 上甚至 sleep(1) 就足够了),当前线程将被置于队列的末尾,其他线程将被处理。

然而,现代操作系统处理这个完全不同的方式,这个建议在今天是多线程实现不佳的标志。 Java 为您提供了设置线程优先级的选项,并允许您轻松使用后台线程进行繁重的处理。今天你会这样实现它:

首先我们需要一个用于低优先级线程的 ThreadFactory:

public class PriorityThreadFactory implements ThreadFactory {

  protected final ThreadGroup allThreads;

  public PriorityThreadFactory(final String name, final int priority, final boolean daemon) {
    this.allThreads = new ThreadGroup(name);
    this.allThreads.setMaxPriority(priority);
    this.allThreads.setDaemon(daemon);
  }

  @Override
  public Thread newThread(final Runnable r) {
    return new Thread(this.allThreads, r);
  }
}

然后我们创建一个ExecutorService,并设置优先级为Thread.MIN_PRIORITY

final PriorityThreadFactory threadFactory = new PriorityThreadFactory("workers", Thread.MIN_PRIORITY, true);
final ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), threadFactory);

最后我们可以提交任意数量的任务,它们将在后台处理,不会阻塞像 UI-Thread 这样的重要线程。

executor.submit(new MyWorkerTask());

【讨论】:

  • 怎么说sleep(1) is enough in win machines!?你能证明这个吗?它取决于很多事情。有时是 50 ns,甚至 10 ms。
  • Windows 95-ME 有一个时间片系统,可以为每个线程授予 10 毫秒的时间片,或者直到它完成/暂停。因此,如果您以任何形式进行了 yield/sleep,则线程恢复的最早时间是 10 毫秒。这在现代 Windows 上已完全改变。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-06-01
  • 1970-01-01
  • 2011-09-18
  • 1970-01-01
  • 2015-11-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多