Java 线程何时超出范围?

Java 线程何时超出范围?,java,multithreading,scope,Java,Multithreading,Scope,我已经编写了一个程序,可以计算文本中的行、字和字符:它通过线程来完成。有时效果很好,但其他时候效果不太好。最终发生的是指向统计的单词和字符数的变量有时短,有时不短 在我看来,线程有时在能够计算出它们想要的所有单词或字符之前就结束了。是因为当while(true)循环中断时,这些线程超出了范围吗 我在下面的问题中包含了一些代码: private void countText() { try { reader = new BufferedReader(new FileReader("th

我已经编写了一个程序,可以计算文本中的行、字和字符:它通过线程来完成。有时效果很好,但其他时候效果不太好。最终发生的是指向统计的单词和字符数的变量有时短,有时不短

在我看来,线程有时在能够计算出它们想要的所有单词或字符之前就结束了。是因为当while(true)循环中断时,这些线程超出了范围吗

我在下面的问题中包含了一些代码:

private void countText() {
  try {
    reader = new BufferedReader(new FileReader("this.txt"));
    while (true) {
      final String line = reader.readLine();
      if(line == null) {break;}
      lines++;
      new Thread(new Runnable() {public void run() {chars += characterCounter(line);}}).start();
      new Thread(new Runnable() {public void run() {words += wordCounter(line);}}).start();
      println(line);
    }

  } catch(IOException ex) {return;}

}

(子问题:这是我第一次问一些问题并发布代码。我不想用StackOverflow代替google和wikipedia,我担心这不是一个合适的问题?我试图让问题更一般化,这样我就不仅仅是在寻求代码方面的帮助……但是,有没有另一个网站可以提供这种信息这个问题可能更合适?

对我来说听起来是个好问题…我认为这个问题可能与chars+=和words+=的原子性有关-几个线程可能会同时调用它-您是否做了任何事情来确保没有交错

即:

线程1的字符数为10,希望添加5

线程2的字符数为10,希望添加3

线程1计算出新的总数,15

线程2计算出新的总数13

线程1将字符设置为15

线程2将字符设置为13


除非您在更新这些变量时使用synchronized,否则可能会出现这种情况。

正如Chris Kimpton已经正确指出的那样,您在不同线程中更新
字符和
单词时遇到问题。在
上同步此
也不起作用,因为
是对当前线程的引用这意味着不同的线程将在不同的对象上同步。您可以使用一个额外的“锁定对象”进行同步,但最简单的解决方法可能是将其用于2个计数器:

AtomicInteger chars = new AtomicInteger();
...
new Thread(new Runnable() {public void run() { chars.addAndGet(characterCounter(line));}}).start();
...
虽然这可能会解决您的问题,但完全正确,原始设计效率非常低


要回答关于线程何时“超出范围”的问题:您将为文件中的每一行启动两个新线程,所有线程都将运行,直到它们到达
run()的末尾
方法。这是除非您创建它们,否则在这种情况下,只要守护进程线程是此JVM中唯一仍在运行的线程,它们就会退出。

不同的线程设计将更容易发现和修复此类问题,并更有效地达成交易。这是一个较长的响应,但总结是“如果您正在用Java执行线程,请尽可能快地进行检查”

我猜您使用多线程处理此代码是为了学习线程,而不是为了加快字数计算,但这是一种非常低效的线程使用方法。您每行创建两个线程-每千行文件创建两千个线程。创建一个线程(在现代JVM中)使用操作系统资源并且通常相当昂贵。当两个(更不用说两千个)线程必须访问共享资源(例如
chars
words
计数器)时,由此产生的内存争用也会影响性能

将计数器变量
同步化
as或
原子化
as可能会修复代码,但这也会使争用的效果更糟。我很确定它会比单线程算法慢

我建议只使用一个长寿命线程来管理
chars
,另一个线程用于
words
,每个线程都有一个工作队列,每次您要添加新编号时,您都可以向其中提交作业。这样,每个变量只有一个线程在写入,如果您对设计进行了更改,那么谁来负责将更加明显。它也会更快,因为没有内存争用,并且不会在一个紧循环中创建数百个线程

同样重要的是,一旦读取了文件中的所有行,在实际打印计数器值之前,要等待所有线程完成,否则会丢失尚未完成的线程的更新。使用当前设计,您必须建立一个创建的线程的大列表,并在最后运行它通过队列和工作线程设计,您可以告诉每个线程耗尽其队列,然后等待完成


Java(从1.5到更高版本)使这种设计非常容易实现:签出。它还使以后添加更多并发性变得容易(假设正确锁定等),因为你可以直接切换到线程池而不是单个线程。

Aha!你看,我完全了解了交错、原子性、同步和锁,但这对我来说永远不会发生。毫无疑问,这正是问题所在!嗯……我使用了同步(这个){关于+=东西}但是我仍然得到了不可预测的结果…哦,天哪,我不认为是这样。我添加了一个println(Thread.activeCount());这会让我感觉到发生了什么。似乎我只是有时候在while循环结束之前激活了全部12个线程。这就是问题所在:时间不够!我猜BufferedReader会将整个文件加载到内存中,如果它是一个小文件,那么读取文件的实际I/O将非常快。因此,您可以快速通过while循环在一瞬间完成,当您离开循环时,您的线程仍在启动。我实现了AtomicInteger,这提高了我的成功率。仍有运行,其中两个计数都低于应有值…您可能没有等待所有线程完成后再打印结果。请参阅下面的答案.我没有等待线程完成。你是对的,我只是想了解一下线程将使用的方法