我编写了一个程序来计算文本中的行,单词和字符:它用线程来完成。它有时很好用,但其他时候却不那么好。最终发生的是指向单词数量的变量,计算的字符有时会变短,有时则不会。
在我看来,线程有时会在他们可以计算他们想要的所有单词或字符之前结束。是因为当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代替谷歌和维基百科,我担心这不是一个合适的问题?我试过了使问题更加通用,以便我不只是在寻求我的代码的帮助......但是,是否有另一个网站,这种问题可能更合适?)
答案 0 :(得分:7)
不同的线程设计可以更容易地找到并解决这类问题,并且更便于讨价还价。这是一个长期的回应,但总结是“如果你正在用Java做线程,请尽快查看java.util.concurrent”。
我猜你是多线程化这段代码来学习线程而不是加速计算单词,但这是一种非常低效的线程使用方式。你要创建两个线程每行 - 一千个线程文件的两千个线程。创建线程(在现代JVM中)使用操作系统资源,并且通常相当昂贵。当两个 - 更不用说两千个线程 - 必须访问共享资源(例如您的chars
和words
计数器)时,由此产生的内存争用也会损害性能。
将计数器变量synchronized
设为Chris Kimpton suggests或Atomic
为WMR suggests可能会修复代码,但这也会使争用效果更糟。我很确定它会比单线程算法慢。
我建议只使用一个长期存在的线程来查找chars
,一个用于words
,每个线程都有一个工作队列,每次要添加新号码时都会向其提交作业。这种方式只有一个线程写入每个变量,如果你对设计进行了更改,那么谁更明显谁负责什么。它也会更快,因为没有内存争用,你不会在紧密循环中创建数百个线程。
一旦你阅读了文件中的所有行,同样重要的是,在实际打印出计数器的值之前,等待所有线程完成,否则你会丢失更新来自尚未完成的线程。使用您当前的设计,您必须建立一个您创建的大量线程,并在最后检查它们是否全部死亡。使用队列和工作线程设计,您可以告诉每个线程耗尽其队列,然后等待它完成。
Java(1.5及以上版本)使这种设计非常容易实现:查看java.util.concurrent.Executors.newSingleThreadExecutor。它还可以让以后更容易添加更多的并发性(假设正确的锁定等),因为您只需切换到线程池而不是单个线程。
答案 1 :(得分:4)
正如Chris Kimpton已经正确指出的那样,您在不同线程中更新chars
和words
时遇到问题。在this
上同步将不起作用,因为this
是对当前线程的引用,这意味着不同的线程将在不同的对象上同步。您可以使用额外的“锁定对象”来进行同步,但解决此问题的最简单方法可能是使用AtomicIntegers作为2个计数器:
AtomicInteger chars = new AtomicInteger();
...
new Thread(new Runnable() {public void run() { chars.addAndGet(characterCounter(line));}}).start();
...
虽然这可能会解决您的问题,Sam Stoke's more detailed answer完全正确,但原始设计效率很低。
回答关于线程何时“超出范围”的问题:您正在为文件中的每一行开始两个新线程,并且所有线程都将运行,直到它们到达run()
方法的末尾。这是除非你创建它们daemon threads),在这种情况下,只要守护程序线程是唯一仍在此JVM中运行的线程,它们就会退出。
答案 2 :(得分:3)
听起来对我来说是一个很好的问题......我认为问题可能与字符的原子性有关+ =和字词+ = - 几个线程可能同时调用 - 你做了什么来保证没有交错。
那是:
线程1,chars = 10,想要添加5
线程2,chars = 10,想要添加3
线程1计算出新的总数,15
线程2计算出新的总数,13
线程1将字符设置为15
线程2将字符设置为13。
除非您在更新这些变量时使用synchronized,否则可能是可能的。