3

静的ユーティリティ メソッドを作成しています。私はメソッドを知ってisEmpty()おりisNew()、スレッドセーフです。getTotal(...)メソッドでは、String と int と共にパラメーターとして使用してStringBuilderいます。StringBuilder可変です。getTotal()スレッドセーフですか? もしそうなら、StringBuilder が変更可能であるにもかかわらず、その理由を説明してください。getCharge()メソッドを呼び出しているため、スレッドセーフかどうかはわかりませんgetTotal()。スレッドセーフかどうかを誰かが判断できますか?

public class NewStringUtils {

    public static boolean  isEmpty(String s){
        return (s == null || s.trim().length()==0);
    }

    public static boolean isNew(String code){
        return( "1009".equals(code) || "1008".equals(code) );
    }
    //StringBuilder is  mutable  -- is the below method threadsafe 
    public static int getTotal(StringBuilder sb,String oldCode ,int a, int b){
        //Is it Threadsafe or not .If so  just bcz every thread invoking this method will have its own copy and other threads can't see its value ?? 
        int k =0;
        if("1011".equals(oldCode) && "1021".equals(sb.toString()) {
            k = a+b;
        }
        return k;
    }
     // is the below method threadsafe 
    public static int getCharge(String code,String oldCode,int charge1,int charge2){
        int total =0;
        StringBuilder sb = new StringBuilder("1021");
        if(!NewStringUtils.isEmpty(code)){

            if(NewStringUtils.isNew(code)){
                //here invoking a static method which has StringBuilder(Mutable) as a parameter
                total  = NewStringUtils.getTotal(sb,oldCode,charge1,charge2);
            }
        }
        return total;
    }
}
4

4 に答える 4

5

getTotalthread-safe2つ以上がthreads同じ参照をStringBuilderメソッドgetTotalに引数として渡し、それを渡す前に変更する可能性があるためではありませんStringBuilder...
そして、ここではそれぞれが独自のオブジェクトのローカルコピーを作成しているgetChargeため、完全にスレッドセーフです. のスレッドセーフについて心配する必要はありません。ThreadSafe ではない 理由を示す短いデモを次に示します。threadStringBuilderstackgetCharge
getTotal

class  ThreadSafe
{
    public static int getTotal(StringBuilder sb,String oldCode ,int a, int b)
    {
        int k =0;
        try
        {
            System.out.println(Thread.currentThread().getName() + " have sb as " + sb);
            Thread.sleep(100);//Added intentionally to show why it is not thread safe.
        }
        catch (Exception ex)
        {
            System.out.println(ex);
        }
        if("1011".equals(oldCode) && "1021".equals(sb.toString())) 
        {
            System.out.println(Thread.currentThread().getName()+" is within if loop");//Thread1 should be within this if block but it's not.
            k = a+b;
        }
        return k;
    }
    public static void main(String[] args) 
    {
        final StringBuilder sBuilder = new StringBuilder();
        Thread th1 = new Thread(new Runnable()
        {
            public void run()
            {
                sBuilder.append("1021");
                getTotal(sBuilder,"1011",10,20);
            }
        },"Thread1");
        Thread th2 = new Thread(new Runnable()
        {
            public void run()
            {
                sBuilder.append("22");
                getTotal(sBuilder,"1011",10,20);
            }
        },"Thread2");
        th1.start();
        th2.start();
    }
}

私のシステムでは、出力は次のとおりです。

Thread1 have sb as 1021
Thread2 have sb as 102122

それが実際にここで発生したThread1前に開始されたと仮定すると(出力に見られるように)、Ifだった場合、ステートメントThread1 は if loop 内にあり、出力に出力されている必要があります。そうではありません 。なぜ?メソッドで(with sb = "1021") がスリープ状態になった後、スレッドは によってプリエンプトされるためです。既存のオブジェクトに追加されます(つまり、現在の新しい値は です)。この場合も、sleep メソッドが に対して呼び出されると、 によってプリエンプトされます。を構築しますが、今までの内容は に変更されています。したがって、条件が false になった場合。そして、それはその行を印刷しませんThread2getTotalthread-safeThread1getTotalThread2Thread222StringBuildersBuilder102122Thread2Thread1Thread1ifsBuilder102122Thread1Thread1 is within if loopgetTotalスレッドセーフであると考えれば、これは予想していたものです。getTotalしたがって、スレッドセーフではないことが証明されます。

どうすればgetTotalスレッドセーフにできますか?

  1. Stringの代わりに渡すことによってStringBuilder
于 2013-03-28T19:28:25.620 に答える
3

getTotal() は、再入可能であるという点でスレッドセーフです (つまり、複数のスレッドから同時に呼び出すことができます)。ただし、ジョブを正しく実行するという点では、別のスレッドが「sb」引数を変更する場合、getTotal() はスレッドセーフではありません。この条件付き安全性は、そのメソッドのドキュメントに明示的に記載する必要があります。

getTotal() が public ではなく private で、getCharge() によってのみ呼び出される場合、無条件にスレッドセーフになります。

于 2013-03-28T19:46:08.550 に答える
1

問題getTotalは、別のスレッドがStringBuilderパラメーターを変更する可能性があることです。しかし、あなたはすでにそれを知っています。は通常、とStringBuilderの 2 つのフィールドを持つクラスとして実装されます。Java メモリ モデルは、1 つのスレッドからのこれらの変数への変更が他のスレッドに同じ順序で発行されることを保証しません。これにより、一部のスレッドよりも大きくすることさえ可能になります。この場合、例外が表示されることがあります。つまり、 の最新の値を気にしなくても、問題が発生する可能性があります。別のケースでは、変更スレッドの値が異なる場合でも、(たとえば) 0 として観測されることがあります。これは、予期しない結果につながります。int countchar[] valuecountvalue.lengthStringBuildercount

于 2013-03-28T20:09:18.430 に答える
1

以下は、Kevin が提案したいことです。これはスレッドセーフです:

//StringBuilder is  mutable  -- is the below method threadsafe 
public static int getTotal(String sb,String oldCode ,int a, int b){
    StringBuilder sb1 = new StringBuilder(sb); //we build out a new StringBuilder each time
    int k =0;
    if("1011".equals(oldCode) && "1021".equals(sb1.toString()) {
        k = a+b;
    }
    return k;
}
于 2013-03-28T19:53:35.427 に答える