【发布时间】:2013-09-03 21:04:42
【问题描述】:
我想写一个 Eratosthenes 筛子,它可以使用特定数量的线程。我发现,它将以下列方式工作: 对于 2 个线程,最多 17 个。 Thread-1 取 2,并开始从 List 中删除 2 的倍数。 Parallel Thread-2 需要 3 并且执行相同的操作。之后 Thread-1 需要 5(因为 List 中没有 4), Thread-2 需要 7 等等,直到它们结束。 我写了以下代码:
private List<Integer> array = new ArrayList<Integer>();
private List<Integer> results = new ArrayList<Integer>();
public synchronized void run(){
while(array.size() > 0){
Integer tmp = array.get(0);
for(int i = 1; i < array.size(); i++){
if( (array.get(i).intValue() % tmp.intValue()) == 0)
array.remove(i);
}
results.add(array.get(0));
array.remove(0);
}
}
public void setArray(int x){
for(int i = 2; i < x; i++)
array.add(Integer.valueOf(i));
}
public void printArray(){
for(Integer i: results){
System.out.println(i);
}
}
此代码有效,但我在我的主类中添加了时间测量“工具”:
ThreadTask task = new ThreadTask();
task.setArray(5000);
Long beg = new Date().getTime();
for(int i = 0; i < 3;i++){
new Thread(task).start();
}
Long sleep = 1000L;
Thread.sleep(sleep);// I am sleeping main thread to wait until other Threads are done
task.printArray();
System.out.println("Time is "+(new Date().getTime()-beg-sleep));
问题是用 2 个线程运行这个比用 1 个线程运行慢,3 个线程比 2 个线程慢。谁能解释一下,为什么?
编辑:
关于这一点有一件很重要的事情。我不需要尽可能快地完成它。出于一个原因,我需要它在线程上工作。我的老师想比较运行同一程序与 1、2 .. n 线程的运行时间。结果应该类似于this 图表。
EDIT2:
我已将代码重写为以下
private HashMap<Integer,Boolean> array = new HashMap<Integer,Boolean>();
private int counter = 1;
private int x;
public void run(){
while(counter < x-1){
do{
counter++;
}
while( array.get(counter));
int tmp = counter;
for(int i = tmp; i < array.size(); i+=tmp){
if( i!= tmp)
array.put(i,true);
}
try{
Thread.sleep(0L, 1);
}
catch (Exception e){}
}
}
public void setArray(int x){
this.x = x;
for(int i = 2; i < x; i++)
array.put(i, false);
}
public void printArray(){
for(int i = 2; i < array.size();i++){
if( !array.get(i))
System.out.println(i);
}
}
现在它使用 HashMap,它是这样工作的:
- 用从 2 到 n 的键和假值填充 HashMap。
- 新线程进入基于
counter变量的while 循环。Counter代表当前密钥。 - 在乞求时增加计数器,这样新线程就不会在早先启动的线程的
counter上运行。 - 将
counter值放入临时变量tmp,这样即使另一个线程增加counter我们也可以工作 - 通过将
i与tmp递增(实际上是在i 的倍数上跳跃)来迭代HashMap,并将它们的值设置为true。 - 打印方法中忽略所有具有
true值的键。counter在增加时也会跳过它们。
问题是它仍然较慢有更多线程。现在怎么了?
【问题讨论】:
-
您的运行方法是同步的,因此一次只能运行 1 个线程。
-
如果
run是您的ThreadTask的实际run方法,那么您的两个线程本质上是按顺序工作的,因为该方法是同步的,从而阻止线程并行工作(这是重点使用线程!)。 -
我认为有问题...您说 Thread-1 需要 5,因为列表中没有 4。这仅适用于两个线程,因为 Thread-1 已经删除了 4。但是如果有三个线程呢?如果不确定 Thread-1 是否已经得到它,那么 Thread-3 怎么会知道不拿 4?我想如果 Thread-3 以 4 开头,它仍然可以工作,但它会做多余的工作。否则必须进行一些非常棘手的同步来避免这种竞争条件。我怀疑这是一个适合并行线程的算法。
-
即使您通过跨线程共享工作来使其工作,如果您访问相同的结构,无论是数组还是其他结构,它也永远不会很快。多处理的正确方法是将筛子分成合理大小的页面,并为每个页面分配一个线程,使用前台线程合并结果。这就是我在 C#here 中所做的,效果非常好(分段筛的内存访问效率也更高)。该算法可以很容易地转换为 Java,并且运行速度大致相同。
标签: java multithreading algorithm sieve-of-eratosthenes