2

在下面的代码中,我多次通过线程池调用一个函数。在这个函数中,我通过一个名为 FastestMemory 的全局属性来跟踪函数的最快执行速度。

但是,当我打印出该值时,在线程池循环之后,我得到了原始值,好像全局变量与每次循环迭代更新的变量不同。当我知道 FastestMemory 的值确实被分配给(例如)253475 时,我只返回了 2000000000。

1)我是否需要重新构建此代码以便能够跟踪最快的迭代?

2)我似乎能够非常快地执行此代码,它在四个 Xeon x7550 上每次迭代花费(平均)不到 1 毫秒。这是正常的还是我的时间在某个地方错了?C# 平均需要大约 400 毫秒?!?

public class PoolDemo {

    static long FastestMemory = 2000000000;
    static long SlowestMemory = 0;
    static long TotalTime;
    static long[] FileArray;
    static DataOutputStream outs;
    static FileOutputStream fout;


  public static void main(String[] args) throws InterruptedException, FileNotFoundException {

        int Iterations = Integer.parseInt(args[0]);
        int ThreadSize = Integer.parseInt(args[1]);

        FileArray = new long[Iterations];
        fout = new FileOutputStream("server_testing.csv");

        // fixed pool, unlimited queue
        ExecutorService service = Executors.newFixedThreadPool(ThreadSize);
        ThreadPoolExecutor executor = (ThreadPoolExecutor) service;

        for(int i = 0; i<Iterations; i++) {
          Task t = new Task(i);
          executor.execute(t);
        }

        executor.shutdown();
        service.shutdown();

        System.out.println("Fastest: " + FastestMemory);

        for(int j=0; j<FileArray.length; j++){
            new PrintStream(fout).println(FileArray[j] + ",");
        }
      }

  private static class Task implements Runnable {

        private int ID;

        static Byte myByte = 0;

        public Task(int index) {
          this.ID = index;
        }

        @Override
        public void run() {
            long Start = System.nanoTime();

          int Size1 = 100000;
            int Size2 = 2 * Size1;
            int Size3 = Size1;

            byte[] list1 = new byte[Size1];
            byte[] list2 = new byte[Size2];
            byte[] list3 = new byte[Size3];

            for(int i=0; i<Size1; i++){
                list1[i] = myByte;
            }

            for (int i = 0; i < Size2; i=i+2)
            {
                list2[i] = myByte;
            }

            for (int i = 0; i < Size3; i++)
            {
                byte temp = list1[i];
                byte temp2 = list2[i];
                list3[i] = temp;
                list2[i] = temp;
                list1[i] = temp2;
            }

            long Finish = System.nanoTime();
            long Duration = Finish - Start;
            FileArray[this.ID] = Duration;
            TotalTime += Duration;
            System.out.println("Individual Time " + this.ID + " \t: " + (Duration) + " nanoseconds");


            if(Duration < FastestMemory){
                FastestMemory = Duration;
            }
            if (Duration > SlowestMemory)
            {
                SlowestMemory = Duration;
            }
        }
      }
}
4

3 回答 3

5

问题是您的主线程正在退出而没有等待提交给执行程序的任务终止。

您只需在调用ExecutorService#awaitTermination之后添加对 ExecutorService#awaitTermination 的调用即可实现ExecutorService#shutdown

您将遇到的另一个问题是,您没有考虑static long跟踪最快时间的值的线程安全性。您将需要添加synchronize块或使用 anAtomicLong来获得安全的 Compare-And-Set 操作。

于 2012-04-04T16:55:17.970 回答
1

shutdown()仅拒绝提交到池的新任务,但对已提交和正在运行的任务不执行任何操作。为了等待所有任务完成,您应该调用:

executor.awaitTermination(1, TimeUnit.MINUTES);
service.awaitTermination(1, TimeUnit.MINUTES);

还可以访问FastestMemory并且SlowestMemory必须以某种方式同步,例如:

synchronized(PoolDemo.class) {
    FastestMemory = Math.min(FastestMemory, Duration);
    SlowestMemory = Math.max(SlowestMemory, Duration);
}

顺便说一句,根据 Java命名约定

所有实例、类和类常量都是大小写混合,首字母小写

于 2012-04-04T16:56:17.540 回答
0

首先,您无需等待任何任务完成。您需要等待执行器服务完成。

executor.awaitTermination(...);

此外 executor 和 service 都引用同一个对象,因此无需关闭两者。实际上,我认为没有任何理由甚至有执行人。您调用的所有方法都是 ExecutorService 的一部分。

接下来对长变量的更改不是线程安全的。如果另一个线程在您的比较之后更改了值会发生什么。对于长变量,即使读取和写入也不是原子的。您应该对这些变量使用 AtomicLongs。

您将希望从 AtomicLong 获取当前值,将其与当前值的运行进行比较。如果新值表明应该进行更新,请使用 compareAndSet 确保没有其他人更改了该值。如果其他人更改了它,请再次运行检查。

于 2012-04-04T16:59:26.190 回答