1

可能重复:
for循环中的最终计数器?

下面是我目前正在运行的代码。我希望能够将迭代器 i 传递到新的 Runnable() 代码中。我知道我通常需要 make 变量 final 才能传递给内部类,但这会破坏迭代器的效果。我知道通常我可以尝试使 Runnable 内部类成为真正的类(请原谅我缺乏术语),但是我会失去 CountDownLatches 的效果。

private long generateThreads(int cores){
    if (cores <= 0)
        cores = Runtime.getRuntime().availableProcessors();

    System.out.println("Using " + cores + " threads for processing");

    final ExecutorService exec = Executors.newFixedThreadPool(cores);
    final CountDownLatch ready = new CountDownLatch(cores);
    final CountDownLatch start = new CountDownLatch(1);
    final CountDownLatch done = new CountDownLatch(cores);

    for (int i = 1; i <= cores; i++){
        exec.execute(new Runnable(){

            public void run(){
                ready.countDown();
                try{
                    start.await();
                    //do work
                    }
                } catch (InterruptedException e){
                    Thread.currentThread().interrupt();
                } finally{
                    done.countDown();
                    exec.shutdown();
                }
            }
        });
    }

    long startTime = 0;
    try{
        ready.await();
        startTime = System.nanoTime();
        start.countDown();
        done.await();
    } catch (InterruptedException e){
        e.printStackTrace();
    }
    return System.nanoTime() - startTime;
}

有谁知道给每个线程一个迭代器的方法,或者创建一个显示所有外部类成员的类,特别是 CountDownLatches?

4

4 回答 4

4

只需将值i赋给循环内的最终变量。

for (int i = 1; i <= cores; i++){
    final int iFinal = i;

    exec.execute(new Runnable(){

        public void run(){
            ready.countDown();
            try{
                start.await();
                System.out.println( "i=" + iFinal );
            } catch (InterruptedException e){
                Thread.currentThread().interrupt();
            } finally{
                done.countDown();
                exec.shutdown();
            }
        }
    });
}
于 2013-01-02T20:02:35.723 回答
2

如果我理解正确,您希望可运行对象保留对迭代器的“引用”,以便它可以观察增量。一种线程安全的方法是使用AtomicInteger

final AtomicInteger i = new AtomicInteger(1); 
for (; i.get() <= cores; i.incrementAndGet()){
    exec.execute(new Runnable(){

        public void run(){
            //you can access i here
            int j = i.get();
        }
    });
}

请注意,这假设您仅从iRunnables 中读取 - 如果您还编写它,则条件的行为i.get() <= cores可能是意外的。

于 2013-01-02T19:58:38.080 回答
0

你可以使用这个技巧:

final int[] holder = new int[1]; // the array is final, not its contents

for (int i = 1; i <= cores; i++){
    holder[0] = i;
    exec.execute(new Runnable(){

        public void run(){
            int i = holder[0]; // compiles
            ready.countDown();
            try{
                start.await();
                //do work
                }
            } catch (InterruptedException e){
                Thread.currentThread().interrupt();
            } finally{
                done.countDown();
                exec.shutdown();
            }
        }
    });
}

编译的原因是数组引用final,但数组的内容可以更改。

我不确定这会有多大用处,因为虽然我没有彻底阅读您的代码,但它似乎不是线程安全的 - 线程的执行顺序是不确定的,所以当 Runnable 实际执行时,您的迭代值可能是任何东西.

于 2013-01-02T19:59:24.637 回答
0

这与您的问题背道而驰,但另一种解决方案是使用 Java 的一些函数式编程库。我讨厌编写线程,这些库让你可以简单地写出你试图用高阶逻辑解决的问题,并在幕后处理并发优化。

Guava 和 lambdaJ 很容易使用。

于 2013-01-02T21:55:20.703 回答