25

そんなこと知ってる

コードのブロックを同期するときは、ロックとして使用するオブジェクトのロックを指定します。たとえば、サードパーティ オブジェクトをこのコードのロックとして使用できます。これにより、1 つのオブジェクト内でコードを同期するために複数のロックを設定できます。

ただし、ブロックに引数を渡す必要性がわかりません。String のインスタンスを渡すかどうかは問題ではないため、同期ブロックはブロックに渡されるパラメーターに関係なく完全に機能するため、一部のランダム クラスのインスタンスが同期ブロックに渡されます。

したがって、私の質問は、とにかく同期ブロックが2つのスレッドが同時にクリティカルセクションに入るのを防ぐかどうかです。次に、なぜ引数を渡す必要があるのか​​。(つまり、デフォルトでランダムなオブジェクトのロックを取得します)。

質問が正しく構成されていることを願っています。

次の例を、同期ブロックに対するランダム パラメータを使用して試しました。

public class Launcher {

    public static void main(String[] args) {
        AccountOperations accOps=new AccountOperations();

        Thread lucy=new Thread(accOps,"Lucy");
        Thread sam=new Thread(accOps,"Sam");

        lucy.start();
        sam.start();

    }

}

非静的同期ブロックの使用:

public class AccountOperations implements Runnable{
    private  Account account = new Account();


    public void run(){

        for(int i=0;i<5;i++){

            makeWithdrawal(10);                 
        }
    }

    public  void makeWithdrawal(int amount){
        String str="asd"
        synchronized (str /* pass any non-null object the synchronized block works*/) {
            if(account.getAmount()>10){

                try{
                    Thread.sleep(5000);             
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                account.withdraw(amount);
                System.out.println(Thread.currentThread().getName()+" has withdrawn 10, current balance "+ account.getAmount());
            }else{
                System.out.println("Insufficient funds "+account.getAmount());
            }
        }

    }

}

静的同期ブロックの使用:

public class AccountOperations implements Runnable{
    private static Account account = new Account();


    public void run(){

        for(int i=0;i<5;i++){

            makeWithdrawal(10);                 
        }
    }

    public static void makeWithdrawal(int amount){

        synchronized (String.class /* pass any class literal synchronized block works*/) {
            if(account.getAmount()>10){

                try{
                    Thread.sleep(5000);             
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                account.withdraw(amount);
                System.out.println(Thread.currentThread().getName()+" has withdrawn 10, current balance "+ account.getAmount());
            }else{
                System.out.println("Insufficient funds "+account.getAmount());
            }
        }

    }

}
4

3 に答える 3

38

String のインスタンスを渡すかどうかは問題ではないため、同期ブロックはブロックに渡されるパラメーターに関係なく完全に機能するため、一部のランダム クラスのインスタンスが同期ブロックに渡されます。

パラメータの目的は 2 つあります。

  1. 同じオブジェクトの他のブロックを同期できるため、同じオブジェクトの状態を変更する可能性のある 2 つのコード ブロックがある場合、それらが互いに干渉することはありません。

    例えば:

    public void getSum() {
        int sum = 0;
        synchronized (this.list) {
            for (Thingy t : this.list) {
                sum += t.getValue();
            }
        }
        return sum;
    }
    
    public void addValue(int value) {
        synchronized (this.list) {
            this.list.add(new Thingy(value));
        }
    }
    

    そこでは、スレッド間で両方のアクセスを同期することが重要です。別のスレッドが呼び出している間に、何かを呼び出してリストを踏みつけるlistことはできません。addValuegetSum

  2. これにより、正しい粒度で同期していることを確認できます。インスタンス固有のリソースへのアクセスをシリアライズしている場合、インスタンス間でそれを行うことは意味がありません。複数のスレッドが異なるインスタンスで動作している場合は、ブロックへの複数のスレッドを許可する必要があります。thisそのため、インスタンス固有のリソース (通常は の一部のフィールドthis)、または静的リソースの場合はクラス (通常は一部のクラス フィールド)を同期します。同様にthis、特定のフィールドのみを保護する必要がある場合は、同期する必要はありません。

    例えば:

    // (In MyClass)
    
    public void getThingySum() {
        int sum = 0;
        synchronized (this.thingyList) {
            for (Thingy t : this.thingyList) {
                sum += t.getValue();
            }
        }
        return sum;
    }
    
    public void addThingy(Thingy t) {
        synchronized (this.thingyList) {
            this.thingyList.add(t);
        }
    }
    
    public void getNiftySum() {
        int sum = 0;
        synchronized (this.niftyList) {
            for (Nifty n : this.niftyList) {
                sum += n.getValue();
            }
        }
        return sum;
    }
    
    public void addNifty(Nifty n) {
        synchronized (this.niftyList) {
            this.niftyList.add(t);
        }
    }
    

    this.thingyListそこで、 on this.thingyList、 not thisorへのアクセスを同期しますMyClass.class。あるスレッドが呼び出しgetThingySumているときに別のスレッドが呼び出している場合は問題ないaddNiftyため、同期するのthisはやり過ぎです。


あなたのstr例を再:

public  void makeWithdrawal(int amount){
    String str="asd"
    synchronized (str /* pass any non-null object the synchronized block works*/) {
        if(account.getAmount()>10){

            try{
                Thread.sleep(5000);             
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            account.withdraw(amount);
            System.out.println(Thread.currentThread().getName()+" has withdrawn 10, current balance "+ account.getAmount());
        }else{
            System.out.println("Insufficient funds "+account.getAmount());
        }
    }

}

そこのコメントは正しくありませんnull。インスタンス以外はそのコードを適切に保護しません。上記が機能しているように見える理由は、文字列のインターンです。文字列リテラルは自動的に文字列プールに入れられるため、同じインスタンスがすべてのスレッドで使用されます。(これは、過剰に同期していることを意味します。これは、インスタンス固有ではなく、JVM全体です。)したがって、機能しますが、それは単なるオブジェクトだからではありません。次から変更した場合:Stringintern

String str = "asd";

Object o = new Object();

その上で同期すると、アカウントへのアクセスをシリアル化することは何もしません。

あなたの例では、同期する正しいものはthis.account.

于 2015-04-26T10:51:07.653 に答える
9

とにかく同期ブロックは、2 つのスレッドが同時にクリティカル セクションに入るのを防ぎます。では、なぜ引数を渡す必要があるのでしょうか?

同期ブロックは、渡されたオブジェクトに基づいて停止するスレッドを決定します。渡すオブジェクトは、同期ブロックによって保護されたモニター セクションの識別子として機能します。

プログラムに多くの監視セクションがあり、それらすべてが同時に実行される場合があります。たとえば、同時にアクセスする必要がある 2 つの無関係なコレクションがある場合、コレクションごとに個別の監視セクションを設定できます。この方法では、他のスレッドが既に同じコレクションにアクセスしている場合にのみ、スレッドが停止します。2 つの異なるコレクションにアクセスする 2 つの異なるスレッドは、同時に処理を進めることができます。

最初の例は自明ではありません。これが機能する理由は、文字列オブジェクトが文字列リテラルに初期化されるためです。リテラルのインターンにより、関数に入るすべてのスレッドが同じStringオブジェクトを取得するため、同期ブロックはモニター セクションを適切に保護します。

于 2015-04-26T10:51:44.390 に答える
0

また、インスタンス X のオブジェクトを、(X.wait() を使用して) 待機キューに渡す必要がある場合に備えて、synchronized のパラメーターに渡す必要がある場合があります。次に、X で notify() を呼び出すことにより、別のスレッドから (必要に応じて) オブジェクトに通知することができます。

于 2021-12-20T16:26:28.227 に答える