使用System.out.print与println的多线程问题

时间:2011-09-26 11:39:08

标签: java multithreading

我有以下线程,每200ms只打印一个点:

public class Progress {

    private static boolean threadCanRun = true;
    private static Thread progressThread = new Thread(new Runnable() 
    {
        public void run() {
            while (threadCanRun) {
                System.out.print('.');
                System.out.flush();
                try {
                    progressThread.sleep(200);
                } catch (InterruptedException ex) {}
            }
        }
    });

    public static void stop()
    {
        threadCanRun = false;
        progressThread.interrupt();
    }

    public static void start()
    {
        if (!progressThread.isAlive())
        {
            progressThread.start();
        } else
        {
            threadCanRun = true;
        }
    }

}

我使用此代码启动线程(暂时):

 System.out.println("Working.");
 Progress.start();


 try {
        Thread.sleep(10000); //To be replaced with code that does work.
 } catch (InterruptedException ex) {}

 Progress.stop();

这真是奇怪:

如果我使用System.out.println('.');,则代码完全按预期工作。 (除了我每次都不想要新线路这一事实。)

使用System.out.print('.');,代码等待十秒钟,然后显示输出。

System.out.println

     Print dot, wait 200ms, print dot, wait 200ms etc...

System.out.print

     Wait 5000ms, Print all dots

发生了什么,我该怎么做才能绕过这种行为?

修改

我也试过这个:

private static synchronized void printDot()
{
    System.err.print('.');
}

和printDot()而不是System.out.print('.'); 它仍然无效。

EDIT2:

有趣。此代码按预期工作:

        System.out.print('.');
        System.out.flush();  //Makes no difference with or without
        System.out.println();

这不是:

        System.err.print('.');
        System.err.flush();
        System.out.print('.');
        System.out.flush();

解决方案:问题与netbeans有关。当我从java -jar中将它作为jar文件运行时,它工作正常。

这是我一生中见过的最令人沮丧的错误之一。当我尝试在调试模式下使用断点运行此代码时,一切正常。

6 个答案:

答案 0 :(得分:5)

stdout是行缓冲的。 使用stderr,或在每次打印后刷新PrintStream。

答案 1 :(得分:4)

(这是奇怪的代码 - 有更简洁的方法来编写和管理线程。但是,这不是问题。)

您的IDE必须按行缓冲。尝试直接在命令行上运行它。 (并希望shell也不会缓冲,但不应该。)

答案 2 :(得分:1)

println方法自动刷新输出缓冲区,而不是print方法。如果您想立即查看输出,可以拨打System.out.flush电话。

答案 3 :(得分:1)

我认为这是因为println()方法是同步的

答案 4 :(得分:1)

(这不是答案;提问者大卫要求我跟进关于重写线程的第二点。我只能以这种方式发布代码。)

public class Progress {

  private ProgressRunnable progressRunnable = new ProgressRunnable();

  public void start() {
    new Thread(progressRunnable).start();
  }

  public void stop() {
    progressRunnable.stop();
  }

  private class ProgressRunnable implements Runnable {

    private final AtomicBoolean running = new AtomicBoolean(true);
    @Override
    public void run() {
      while (running.get()) {
        System.out.print('.');
        System.out.flush();
        try {
          Thread.sleep(200);
        } catch (InterruptedException e) {
        }
      }
    }

    private void stop() {
      running.set(false);
    }

  }

  public static void main(String[] args) {
    Progress progress = new Progress();
    progress.start();
    try {
      Thread.sleep(10000);
    } catch (InterruptedException e) {
    }
    progress.stop();
  }

}

答案 5 :(得分:0)

我使用System.out.print()System.out.flush()测试了您的代码。该代码适用于我,代码除外:

while (!threadCanRun)
            {
                 Thread.yield();
            }

进展课程。这样做,线程正在暂停,允许其他线程执行,正如您在线程api page中看到的那样。删除此部分,代码可以正常工作。

但我不明白为什么你需要yield方法。如果您致电Progress.stop(),则会导致调用yield方法。线程将以interrupt停止后(在我的电脑上等待了大量时间之后)。 如果要允许其他线程执行并且当前线程暂停,请考虑join()方法。

如果你想停止当前线程,也许你可以考虑删除 while(!threadCanRun)循环Thread.currentThread().join()Thread.interrupt() stop()之前的p.stop()等待完成其他线程,或只是调用{{1}}方法。

看看这些posts