0

私はスレッドをいじっていますが、なぜこれが思ったように機能しないのかわかりません。

スレッドを使用して合計を計算しようとしていますが、結果を出力するまでにスレッド プールがすべてのタスクが終了するのを待つことを期待していました (shutdown() 呼び出しと isTerminated() チェックによる)。

ここで何が欠けていますか?

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test5 {

private Integer sum= new Integer(0);

public static void main(String[] args) {

    ExecutorService pool = Executors.newCachedThreadPool(); 
    Test5 obj = new Test5();

    for(int i=0; i<1000; i++){
            pool.execute(obj.new Adding());
    }

    pool.shutdown();

    while(!pool.isTerminated()) {
        //could be empty loop...
        System.out.println(" Is it done? : " + pool.isTerminated());
    }

    System.out.println(" Is it done? : " + pool.isTerminated());
    System.out.println("Sum is " + obj.sum);                
}

class Adding implements Runnable {

    public void run() {

        synchronized(this) {

            int tmp = sum;
            tmp+=1;
            sum=new Integer(tmp);           
        }                       
    }               
}
}

良い結果が得られますが、次のような出力も得られます。

Is it done? : true
Sum is 983
4

2 に答える 2

2

メイン オブジェクト インスタンスで同期する必要があります。以下を使用してintいますが、整数も機能します (明示的にゼロに初期化する必要があります)。

これが作業コードです

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class AppThreadsSum {

    int sum;

    public static void main(String[] args) {

        ExecutorService pool = Executors.newCachedThreadPool();

        AppThreadsSum app = new AppThreadsSum();

        for (int i = 0; i < 1000; i++) {
            pool.execute(app.new Adding());
        }

        pool.shutdown();

        while (!pool.isTerminated()) {
            System.out.println(" Is it done? : " + pool.isTerminated());
        }

        System.out.println(" Is it done? : " + pool.isTerminated());
        System.out.println("Sum is " + app.sum);
    }

    class Adding implements Runnable {

        public void run() {

            synchronized (AppThreadsSum.this) {

                sum += 1;
            }
        }
    }
}

psビジー待機は避けるべきアンチパターンです(完了し、この重要なことを認識するために隣人の回答からコピーされます、コメントを参照してください)

于 2013-04-18T14:23:00.630 に答える
1

いくつかの問題があります。

  1. あなたのコードはスレッドセーフではありません
  2. ビジー待機は避けるべきアンチパターンです。

1. とはどういう意味ですか?

A と B の 2 つのスレッドがあるとします。

  1. Aは 1とsumして読み込みますtmp
  2. Bは 1とsumして読み込みますtmp
  3. Asumは 2 に増加します
  4. sumA は2と書きます
  5. Bsumは 2 に増加します
  6. sumB は2と書きます

したがって、2 回インクリメントすると 2 になります。いいえ、まったくそうではありません。

「しかし、私は を使用しましたがsynchronized、これは起こるべきではありません」と言うかもしれません。まあ、あなたはしていません。

Addingインスタンスを作成するときは、newそれぞれを作成します。1000 個の個別のAddingインスタンスがあります。

すべてではなくsynchronized(this)、現在のインスタンスで同期している場合。したがって、同期ブロックは何もしません。Adding

さて、簡単な解決策は を使用することsynchronized(Adding.class)です。

これsynchronized(Adding.class)により、コード ブロックがすべてのAddingインスタンスで正しく同期されます。

これは原子的に増加し、まさにこの種のタスク用に設計されているため、良いAtmoicInteger解決策は anではなく anを使用することです。Integer

では2へ。

あなたはループwhile(thing){}を持っています. これは、 CPU サイクルの膨大な浪費です。Anには、シャットダウンするまで待機する特別なブロッキング メソッドがあります。thingtrueExecutorServiceawaitTermination

次に例を示します。

static final AtomicInteger sum = new AtomicInteger(0);

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

    ExecutorService pool = Executors.newCachedThreadPool();

    for (int i = 0; i < 1000; i++) {
        pool.execute(new Adding());
    }

    pool.shutdown();
    pool.awaitTermination(1, TimeUnit.DAYS);

    System.out.println(" Is it done? : " + pool.isTerminated());
    System.out.println("Sum is " + sum);
}

static class Adding implements Runnable {

    public void run() {
        sum.addAndGet(1);
    }
}

また、この状況では a を使用しないことをお勧めcachedThreadPoolします。1000 個の s が送信されており、CPU よりもRunnableはるかに多くの s が生成されるためです。適切な数のsThreadを使用することをお勧めします。newFixedThreadPoolThread

intリテラルの使用については触れませんがInteger、なぜリテラルnew Integer()が必要ないのかについては説明しません。

于 2013-04-18T14:17:48.037 に答える