gpt4 book ai didi

java - 熟悉 Java 中的线程 : Why does this program's runtime increase with increasing number of threads

转载 作者:行者123 更新时间:2023-11-30 07:10:04 27 4
gpt4 key购买 nike

情况

我正在尝试熟悉 Java 中的线程。出于这个原因,我修改了我在一本书中找到的程序列表。所做的事情非常简单:

  1. 它创建一个包含 100.000.000 个元素的 boolean[] 数组。
  2. 它使用 NUMBER_OF_SERVERS 个线程用 truefalse 随机填充该数组的元素。
  3. 最后,它使用 NUMBER_OF_SERVERS 个线程扫描该数组,并计算有多少条目设置为 true

有关详细信息,请参阅本文底部的以下代码。

问题

当我用不同数量的线程运行代码并测量运行时间时,我得到了一个非常奇怪的结果;或者至少是我不理解的行为:当我使用更多线程时,BuildService-Thread 会消耗更多运行时。在一个线程中构建整个数组(基于随机 true 分布)大约需要 10 秒。接下来,当我使用四个线程时,我希望运行时间会减少。但是,我得到了大约 17 秒的时间消耗。

我的 ScanService 按预期工作:时间消耗随着线程的增加而减少。

详情请看下图:

Thread-Runtime with random <code>true</code>-distribution

但是,如果更改我的代码中的一行并替换 if ((int) ((Math.random() * 2d)) == 0)-语句(对于随机 true-distribution) 和 if (i % 2 == 0) (因此,每第二个项目都是真的)我得到了我期望的行为:

Thread-Runtime with even <code>true</code>-distribution

问题

所以,我的问题是:

  1. 为什么在使用 Math.random() 函数时更多的线程会导致更长的运行时间?
  2. 反之亦然,为什么仅使用一个使用完全相同函数的线程时运行时间会减少?
  3. 在处理线程时,可以从这种行为中得出什么“一般规则”?

背景信息

代码在英特尔酷睿 i3 上运行。

代码

public class AsynchService
{
private static final int ARRAY_SIZE = 100000000; //100.000.000
private static final int NUMBER_OF_SERVERS = 16;
private static final int HOW_MANY = ARRAY_SIZE / NUMBER_OF_SERVERS;

//build array asynch
public static boolean[] buildArrayAsynch()
{
//build array with NUMBER_OF_SERVERS-Threads
boolean[] array = new boolean[ARRAY_SIZE];
Thread[] buildServerThread = new Thread[NUMBER_OF_SERVERS];

long startTime = System.currentTimeMillis();

for (int i = 0; i < NUMBER_OF_SERVERS; i++)
{
int start = i * HOW_MANY;
int end = (i != NUMBER_OF_SERVERS - 1) ? (i + 1) * HOW_MANY - 1 : ARRAY_SIZE - 1;

buildServerThread[i] = new BuildService(array, i, start, end);
}

//synchronize and wait for result
int expectedResult = 0;

for (int i = 0; i < NUMBER_OF_SERVERS; i++)
{
try
{
buildServerThread[i].join();
}
catch (InterruptedException ex) {}

expectedResult += ((BuildService) buildServerThread[i]).getExpectedResult();
}

System.out.println("\nNumber of \"true\"s ==> Expected result: " + expectedResult);
System.out.println("Build duration: " + (System.currentTimeMillis() - startTime) + " ms\n");

return array;
}

//scan array asynch
public static int scanArrayAsynch(boolean[] array)
{
//create services and server-threads
Thread[] serverThread = new Thread[NUMBER_OF_SERVERS];

long startTime = System.currentTimeMillis();

for (int i = 0; i < NUMBER_OF_SERVERS; i++)
{
int start = i * HOW_MANY;
int end = (i != NUMBER_OF_SERVERS - 1) ? (i + 1) * HOW_MANY - 1 : ARRAY_SIZE - 1;

serverThread[i] = new ScanService(array, i, start, end);
}

//synchronize with servers, wait for server end
int result = 0;

for (int i = 0; i < NUMBER_OF_SERVERS; i++)
{
try
{
serverThread[i].join();
}
catch (InterruptedException ex) {}

result += ((ScanService) serverThread[i]).getResult();
}

System.out.println("Search duration: " + (System.currentTimeMillis() - startTime) + " ms");
return result;
}

public static void main(String[] args)
{
//build array
boolean[] array = buildArrayAsynch();

//scan array
int result = scanArrayAsynch(array);

//display result
System.out.println("\nResult: " + result);

}
}

class BuildService extends Thread
{
private boolean[] array;
private int start;
private int end;
private int expectedResult = 0;

public BuildService(boolean[] array, int serviceId, int start, int end)
{
this.array = array;
this.start = start;
this.end = end;

this.setName("BuildService " + serviceId);

this.start();
}

public int getExpectedResult()
{
return expectedResult;
}

public void run()
{
if (start < 0 || end >= array.length) throw new IndexOutOfBoundsException();

System.out.println(getName() + ": StartIndex = " + start + "; EndIndex = " + end);

long startTime = System.currentTimeMillis();

for (int i = start; i <= end; i++)
{
//if (i % 2 == 0)
if ((int) ((Math.random() * 2d)) == 0)
{
array[i] = true;
expectedResult++;
}
else
{
array[i] = false;
}
}

System.out.println(getName() + " finished! \"true\" elements: " + expectedResult + "; duration = " + (System.currentTimeMillis() - startTime) + "ms");
}
}

class ScanService extends Thread
{
private boolean[] array;
private int serviceId;
private int start;
private int end;
private int result = 0;

public ScanService(boolean[] array, int serviceId, int start, int end)
{
this.array = array;
this.serviceId = serviceId;
this.start = start;
this.end = end;

this.start();
}

public int getResult()
{
return result;
}

public void run()
{
if (start < 0 || end >= array.length) throw new IndexOutOfBoundsException();

System.out.println("Server " + serviceId + ": StartIndex = " + start + "; EndIndex = " + end);

for (int i = start; i <= end; i++)
{
if (array[i]) result++;
}
}
}

最佳答案

细节决定成败。 documentation Math.random() 的答案是:

This method is properly synchronized to allow correct use by more than one thread. However, if many threads need to generate pseudorandom numbers at a great rate, it may reduce contention for each thread to have its own pseudorandom-number generator.

要解决这个问题,请尝试为您的 BuildService 类的每个实例创建一个 java.util.Random 实例,并使用它代替 Math.random()。使用 java.util.Random 的一个好处还在于您不必进行不必要的双整数运算,而是可以简单地使用 nextBoolean() 方法。

关于java - 熟悉 Java 中的线程 : Why does this program's runtime increase with increasing number of threads,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22534073/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com