Java Threads的工作频率

时间:2015-06-17 13:20:29

标签: java multithreading

我想在显示“加载动画文字”时加载一些文件。

我使用一个线程来加载文件,我的主线程用于显示动画。

问题在于,在某些测试中,我注意到错误工作时间错误有时我的动画真的冻结了< /强>

代码示例:这是一个例子,我的代码太长了,但这是我的代码加载文件的主要形式:

for (int i = 0; i < file.size; i++) {
     //Do soem stuff
}

示例:( A =加载文件线程; B =文本动画)

A
 B
 B
 B
 B
 B
A
A
A
A
A

我想要的是什么:

A
 B
A
A
 B
 B
A
A
 B
A
 B
A
 B
A

我的问题是:有没有办法让线程同时工作?

修改

我的代码太长(超过500行),但做一个简单的代码我得到了同样糟糕的结果:

例证

package thread_test;

public class TestThread extends Thread {


  public TestThread(String name) {
    super(name);
  }

  public void run() {
    for(int i = 0; i < 30; i++)
        System.out.println(this.getName());
  }       
}

主要课程

package thread_test;

public class Test {

public static void main(String[] args) {

    TestThread t = new TestThread("A");
    TestThread t2 = new TestThread(" B");

    t.start();
    t2.start();

}

}

结果:

A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
A
 B
 B
 B
 B
 B
 B
 B
 B
 B
 B
 B
 B
 B
 B
 B
 B
 B
 B
 B
 B
 B
 B
 B
 B
 B
 B
 B
 B
 B
 B

你可以看到我面临同样的问题,所以如果我们能用给定的代码解决这个问题,我想我可以解决这个问题。

3 个答案:

答案 0 :(得分:2)

在线程调度方面,Java的保证很少:例如,无法保证从线程池中选择线程的顺序。

您无法控制调度程序,但可以影响调度程序,例如使用

如果您所描述的线程生命周期(即两个线程获得相同的时间但应更频繁地切换),请尝试向两个线程添加一些Thread.yield()

但是在大​​多数情况下,当调度程序执行奇怪的事情时,问题是有一个线程几乎占用了所有的cpu时间。

答案 1 :(得分:1)

我添加了Thread.sleep(1),输出就像你想要的那样

试试这个运行方法:

Function CheckDriveExists(drive)

    CheckDriveExists = false

    If FSO.DriveExists(drive) Then  ' A drive is mapped for the required 'drive'

        '** Create a file name for the test file *'
        Dim tfDate, tfName
        tfDate = Year(Now) & Month(Now) & Day(Now) & Hour(Now) & Minute(Now) & Second(Now)
        tfName = "tstfile-" & tfDate & ".bucf"

        '** Try to create and then delete a file on the usrs backup drive *'
        On Error Resume Next
        FSO.CreateTextFile(drive & ":\" & tfName)
        FSO.DeleteFile(drive & ":\" & tfName)

        CheckDriveExists = (Err.Number = 0) ' Check to see if the file was created and deleted successfully
        Err.Clear                           ' Clear any possible error              
        On Error GoTo 0                     ' Reset error handling

    End If

End Function

输出是:

  public void run() {
    for (int i = 0; i < 30; i++) {
        System.out.println(this.getName());
        try {
        Thread.sleep(1);
        } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        }
    }
    }

为什么这会起作用的原因是我已经给出了Thread.sleep(1)以便将当前线程休眠并给予其他更改以执行,其他线程也会发生同样的事情。

  

注意:线程调度程序是JVM的一部分   决定哪个线程应该在任何给定时刻运行,也需要   线程退出运行状态。中的任何线程   调度程序可以选择runnable状态   运行线程。可运行的顺序   选择运行的线程无法保证。虽然排队行为   是典型的,不保证。队列行为意味着当a   线程已完成其“转弯”,它移动到线的末尾   可运行的池并等待它最终到达前面   该行,可以再次选择。事实上,我们称之为   可运行的池,而不是一个可运行的队列,以帮助加强   事实上,线程并没有按照一些有保证的顺序排列

以上注释来自此Stackoverflow问题: Thread swaps in and out automatically without any yield or sleep

答案 2 :(得分:1)

(有点长篇文章,但我希望它可以帮助您了解正在发生的事情)

好吧,正如一些评论已经暗示的那样,很难观察到如此快速执行的如此少量代码的交错线程执行。

虽然这并非不可能,尤其是在多CPU /核心机器上,您仍然可以观察到样品的某些并行执行。但是你必须多次运行它,也许你会很幸运。

直观地说,当我们想到并行执行线程时,我们希望认为每个线程只有在将执行权交给另一个线程之前才能运行一行代码。

但那并不是操作系统如何安排线程执行。实际上,这样做是一个坏主意,因为每次操作系统切换上下文以允许执行不同的线程时,都会产生显着的性能成本。因此,相反,操作系统实现并行执行的方式是在另一个线程转向执行之前为每个线程提供X个时间量。这个分配的时间片是所谓的quantum。并且线程可以执行任意数量的代码行而不会中断(来自其他线程),只要它可以在分配的时间片或量程内运行它们。当时间到时,OS切换执行上下文以允许另一个线程执行。 (这是一个非常简化的解释,但希望它可以帮助您获得基本想法)

要了解为什么以这种方式执行并行执行是有意义的,而不是为每一行执行切换线程,让我们考虑以下内容,使用完全伪造的数字(并简化事情,让我们来做假设我们有一个单独的CPU。它会变得更复杂):

  • 让我们说每行执行需要1毫秒才能运行。
  • 我们假设我们有线程A有1000行代码要执行(1000毫秒的工作)
  • 假设我们有线程B只有100行代码要执行(100毫秒的工作)
  • 最后,让我们说操作系统执行上下文切换需要10毫秒(从一个线程切换到另一个线程)

我们可以让线程执行哪些不同的工作方式?

选项1:允许线程在切换到另一个线程之前完全完成工作

  1. 执行线程A完成:这需要1000毫秒。
  2. 切换上下文以允许线程B执行:这需要10毫秒。
  3. 执行线程B完成:这需要100毫秒。
  4. 总执行时间:1110毫秒。 非常好的时间。

    还不错!但是,从观察线程B的人的角度来看,在最终做任何事情之前,线程似乎冻结了一秒钟。因此,这种看法可能有些消极。

    现在让我们来看看另一个极端...

    选项2:每个线程在切换到另一个线程之前执行一行代码(类似于您在帖子中提到的内容)

    循环执行100次:

    1. 从线程A执行一行代码:1 ms。
    2. 切换上下文以允许线程B执行:这需要10毫秒。
    3. 从线程B执行一行代码:1 ms。
    4. 切换上下文以允许线程A执行:这需要10毫秒。
    5. 此时,线程B已完成,这已经花费了2200毫秒。现在,我们仍然需要运行其余的线程A的工作,这是900毫秒。

      所以我们有一个总计: 3100毫秒的执行时间。

      显然,这并不像第一种选择那样理想。请记住,此示例假装我们只运行这两个线程。但在现实世界中,这两个线程是从各种其他进程运行的更大线程池的一部分。操作系统也必须对这些线程公平。因此,如果操作系统使用这种方法并行执行线程,您可以看到由于所有这些上下文切换而浪费的时间量。

      然而,从某人观察线程B的角度来看,至少它似乎从未被冻结过。它非常非常慢,但从未冻结过。那么,这是否意味着我们有一个快乐的用户?可能不是。

      最后,让我们来看一下操作系统将使用的典型方法:

      选项2:每个线程获得固定的时间片或量子

      让我们说OS预定量程为50 ms。 (在我们使用虚假测量的示例中,这意味着每个线程可以不间断地执行50行代码)然后两个线程的并行执行将如下所示:

      1. 执行线程A 50毫秒。
      2. 切换到线程B:10毫秒。
      3. 执行线程B 50毫秒。
      4. 切换到线程A:10毫秒。
      5. 执行线程A 50毫秒。
      6. 切换到线程B:10毫秒。
      7. 执行线程B 50 ms。 &lt; - 线程B在这里完成。
      8. 切换到线程A:10毫秒。
      9. 执行线程A 900毫秒。
      10. 总执行时间:1140毫秒。

        因此,正如您所看到的,总执行时间比尝试将线程与每个执行行交错要好得多。然而,通过在时间片中划分工作,没有一个线程停止工作超过60毫秒,这是一个很小的数字,从用户的角度来看,感觉就像线程B从未凝固了。

        因此,基本上,操作系统正在尽最大努力在提供良好的总执行时间和提供响应式用户体验之间找到一个甜蜜的中间立场。它通过设置适当的量子大小来实现这一点。量子有多大?嗯,这取决于很多因素和操作系统,并且通常可以调整值,如果你真的,真的必须。 (更多关于这篇文章最后的内容)但我的理解是,典型的量子值可能会在20毫秒到100毫秒之间变化,这取决于很多因素。

        <强>结论

        希望您现在了解操作系统使用时间片 它的工作方式,以及为什么你没有在你的简短示例程序中观察线程交错。

        但要想明白这一点,你必须在每个线程中运行的代码量非常小而且执行起来很快,它完全适合单个量子或时间片。所以操作系统没有打扰中断线程A,因为它认为执行上下文切换不值得花费。

        也就是说,在大型程序中,每个线程需要执行更多工作,然后在良好的实际性能和感知性能之间找到正确的平衡可能会成为一个问题。

        通常,操作系统可以很好地确定负责向用户显示内容的线程的优先级。尽管如此,如果真的有必要,通常还是有办法改变量子设置以稍微改变平衡。

        Windows操作系统中的一个示例是My PC - &gt; Properties - &gt; Advanced system settings - &gt; Advanced tab - &gt; Performance settings...。您可以在Adjust for best appearanceAdjust for best performance之间进行选择。现在你可以毫无疑问地猜测,这个设置调整了封面下的量子大小来调整线程执行平衡。

        我希望这会有所帮助。