Synchronizedロックされたオブジェクト

3873 ワード

synchronizedはjavaで同期に使用されるキーワードで、一般的にSynchronizedでオブジェクトをロックしてスレッド同期を行います.プログラム実行中にsynchronizedがロックされているオブジェクトがどのオブジェクトなのかを知る必要があります.そうしないと、マルチスレッドのプログラムで問題が発生する可能性があります.
次のコードを見て、私たちは静的変数nを定義しました.runメソッドでは、nを10増加させ、mainメソッドでは、n増加の操作を実行するために100スレッドを開きました.スレッドが同時実行されていない場合、nの最後の値は1000であるべきです.明らかに、次のプログラムの実行は1000ではありません.スレッド同期を行っていないからです.
import java.util.concurrent.TimeUnit;

public class SynchronizedTest1 extends Thread {
    public static int n = 0;

    public void run() {
        try {
            // n  10 
            for (int i = 0; i < 10; i++) {
                n = n + 1;
                TimeUnit.MILLISECONDS.sleep(10);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread[] threads = new Thread[100];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new SynchronizedTest1();
            threads[i].start();
        }

        //          ,     main  ,     n      
        for (Thread thread : threads) {
            thread.join();
        }
        System.out.println(n);
    }
}

同期を実現するために,上記のコードを修正し,以下のようにincreaseメソッドを追加した.しかし、次のコードを実行すると、nは1000ではないことがわかります.
import java.util.concurrent.TimeUnit;

public class SynchronizedTest2 extends Thread {
    public static int n = 0;

    public synchronized void increase() {
        n++;
    }
    public void run() {
        try {
            // n  10 
            for (int i = 0; i < 10; i++) {
                increase();
                TimeUnit.MILLISECONDS.sleep(10);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread[] threads = new Thread[100];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new SynchronizedTest2();
            threads[i].start();
        }

        //          ,     main  ,     n      
        for (Thread thread : threads) {
            thread.join();
        }
        System.out.println(n);
    }
}

実は理由は簡単で、上の複数のスレッドは実行時に同じオブジェクトロックを競合していません.synchronizedで修飾された非静的メソッドを実行すると、スレッドはまずこのメソッドを呼び出すオブジェクトのロックを取得し、コードを実行し続けることができます.では、このメソッドを呼び出すのはいったいどのオブジェクトなのか、thisオブジェクトです.上記の例では、thread[i]が表すスレッドが取得するロックオブジェクトはthread[i]オブジェクト、すなわちそのスレッドオブジェクト自体である.したがって,上記で開発した100スレッドは,自身のオブジェクトを取得すれば実行でき,同期が機能しなくなる.
コードを再修正します.increaseメソッドをi静的に変更します.プログラム実行後のnの値は1000です.
import java.util.concurrent.TimeUnit;

public class SynchronizedTest3 extends Thread {
    public static int n = 0;

    public synchronized static void increase() {
        n++;
    }
    public void run() {
        try {
            // n  10 
            for (int i = 0; i < 10; i++) {
                increase();
                TimeUnit.MILLISECONDS.sleep(10);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread[] threads = new Thread[100];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new SynchronizedTest3();
            threads[i].start();
        }

        //          ,     main  ,     n      
        for (Thread thread : threads) {
            thread.join();
        }
        System.out.println(n);
    }
}

synchronizedがstaticメソッドを修飾すると、特定のオブジェクトではなくクラスのClassオブジェクトがロックされます.上の例では、SynchronizedTest 3.classオブジェクトがロックされています.プログラム実行中、クラスのClassオブジェクトは1つしかないので、上のスレッドは同じオブジェクトロックを競合しています.
synchronizedロック対象のまとめです.
(1)同期メソッドの場合、現在のオブジェクトをロックする(this)
(2)静的同期方法に対して、現在のクラスのClassオブジェクトをロックする
(3)同期コードブロックに対してsynchronizedカッコ内のオブジェクトをロックする