3

次のコードは、出力結果が小さいものから大きいものへの順序ではないことを発見しました。小さいものから大きいものへの順序であることを保証するにはどうすればよいですか?

Java コード

public class TestSync {  

    /** 
     * @param args 
     */  
    public static void main(String[] args) {  
        for (int i = 0; i < 10; i++) {  
            new Thread(new Thread1()).start();  
        }  

    }  

    public int getNum(int i) {  
        synchronized (this) {  
            i++;  
        }  
        return i;  
    }  

    static class Thread1 implements Runnable {  
        static Integer value = 0;  
        @Override  
        public void run() {  
            TestSync ts = new TestSync();  
            value = ts.getNum(value);  
            System.out.println("thread1:" + value);  
        }  
    }  

}  
4

6 に答える 6

2

なぜこれが必要なのか不思議に思うかもしれませんが、これを行う1つの方法があります。エレガントではありませんが、元のプログラムへの最小限の変更を表しています。

import java.util.concurrent.*;

public class TestSync {

    public static void main(String[] args) {

    ExecutorService service = Executors.newSingleThreadExecutor();
    for (int i = 0; i < 10; i++) {
        service.submit(new Thread1());
    }

}

public int getNum(int i) {
    synchronized (this) {
        i++;
    }
    return i;
}

static class Thread1 implements Runnable {
    static Integer value = 0;
    @Override
    public void run() {
        TestSync ts = new TestSync();
        value = ts.getNum(value);
        System.out.println("thread1:" + value);
    }
}

}

これがより良いバージョンです。カウンターにAtomicIntegerを使用して(この場合はおそらくやり過ぎです)、不快なgetNum()メソッドを削除します。

import java.util.concurrent.*;
import java.util.concurrent.atomic.*;

public class TestSync {  
    static private AtomicInteger i = new AtomicInteger(0);

    public static void main(String[] args) {  
        ExecutorService service = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {  
            service.submit(new MyThread(i));  
        }  
        try { Thread.sleep(2*1000); } catch(Exception ex) {}
        service.shutdown();
    }  

    static class MyThread implements Runnable {  
        private int num = 0;
        public MyThread(int num) {
            this.num = num;
        }
        @Override  
        public void run() {  
            int value = i.incrementAndGet();
            System.out.println("thread # " + num + " value = " + value);  
        }  
    }  
}  
于 2012-08-28T04:00:48.867 に答える
2

何を達成しようとしていますか?コードは、特定のTestSyncインスタンスに対して行われた呼び出しのみを同期しています。各スレッドは独自のインスタンスを作成するため、何も同期していないように見えます。あなたのコードは、異なるスレッド間でアクセスを同期または調整するために何もしていません。

次のコードは、達成しようとしていることに沿っている可能性があることをお勧めします。

public static void main (String[] args) throws java.lang.Exception {
        for (int i = 0; i < 10; i++) {  
            new Thread1().start();  
        }  
}

//no need for this to be an instance method, or internally synchronized
public static int getNum(int i) {  
       return i + 1;  
}

static class Thread1 extends Thread {  
    static Integer value = 0;  

    @Override  
    public void run() {  
        while (value < 100) {
            synchronized(Thread1.class) {  //this ensures that all the threads use the same lock
                value = getNum(value);  
                System.out.println("Thread-" + this.getId() + ":  " + value);  
            }

            //for the sake of illustration, we sleep to ensure some other thread goes next
            try {Thread.sleep(100);} catch (Exception ignored) {} 
        }
    }  
}

実例はこちら: http://ideone.com/BGUYY

getNum()本質的に不要であることに注意してください。上記の例はvalue = getNum(value);、単純な に置き換えても同じように機能しますvalue++;

于 2012-08-28T04:04:51.737 に答える
1

言うまでもなく、スレッドでの実行時にどの順序コードが実行されるかは保証されていません。これは、タイムスライスと呼ばれるものが原因です。CPU は、時間の「スライス」を特定のスレッドに割り当てます。スライスが起動すると、スレッドが事実上一時停止し、他のスレッドがスライスを取得できるようになります。最終的には、一時停止されたスレッドに戻り、追加のタイムスライスが提供されますが、それは実際には CPU 次第です。

複数のコアやハイパースレッディングを使用すると、CPU はより多くのスレッドに同時にタイムスライスを与えることができます。

ただし、前述したように、スレッドがどの順序でタイムスライスされ、個々のスレッドがいつどこで一時停止して再開されるかは保証されません。

これが意味することは、「i++」操作が (関連する印刷と共に) 実行される順序は、必ずしもスレッドを開始する順序とは限らないということです。さらに、スレッド間の共有変数は、スレッド レベルでの値のキャッシュを防ぐために、"volatile" 修飾子を使用して実際に宣言する必要があります。

順次順序付けを強制したい場合は、そもそもなぜ順次ループの代わりにスレッドを使用しているのかを疑問視する必要があります。

于 2012-08-28T04:10:27.173 に答える
0

私は Java プログラマーではありませんが、同期ロックは getNum() が順不同で呼び出されるのを防ぎません。また、Thread1 は個別のスレッドで実行されるため、スケジューラは 10 個のスレッドを任意の順序で自由に実行できます。

同期によって保証されるのは、一度に 1 つのスレッドのみが同期セクション内のコードを実行できるということです。

順次実行する場合は、1 つのスレッドですべての getNums() を呼び出します。または、ExecutorService のようなスレッド キューを使用します。

于 2012-08-28T04:04:28.330 に答える
0

main()メソッドを次のように書き換えます。

public static void main(String[] args)
{
    for (int i = 0; i < 10; i++)
    {
        Thread th = new Thread(new Thread1());
        th.start();
        try
        {
            th.join();
        }
        catch (InterruptedException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
于 2013-02-06T02:30:15.487 に答える
0

実際には2つの問題があります。

1)。ブロックはインスタンスSynchronizationに固有ですThread1

どちらかを使用する必要があります

 synchronized(TestSync.class) {  
                //  
                //
            }

また

synchronized(Thread1.class) {  
                //  
                //
            }

2)。核心的な問題はそれです。 また、値が順番になるように同期する必要がありSetting the static variableます 。System.out.println()generationprinting

于 2012-08-28T04:43:41.333 に答える