7

次のコード ( Java Concurrency in Practice Chapter 2、セクション 2.5、リスト 2.8 からコピー):

@ThreadSafe
public class CachedFactorizer implements Servlet {
    @GuardedBy("this") private BigInteger lastNumber;
    @GuardedBy("this") private BigInteger[] lastFactors;
    @GuardedBy("this") private long hits;
    @GuardedBy("this") private long cacheHits;

    public synchronized long getHits() { return hits; }

    public synchronized double getCacheHitRatio() {
        return (double) cacheHits / (double) hits;
    }

    public void service(ServletRequest req, ServletResponse resp) {
        BigInteger i = extractFromRequest(req);
        BigInteger[] factors = null;
        synchronized (this) {
            ++hits;
            if (i.equals(lastNumber)) {
                ++cacheHits;
                factors = lastFactors.clone(); // questionable line here
            }
        }
        if (factors == null) {
            factors = factor(i);
            synchronized (this) {
                lastNumber = i;
                lastFactors = factors.clone(); // and here
            }
        }
        encodeIntoResponse(resp, factors);
    }
}

factorslastFactors配列が複製されるのはなぜですか? factors = lastFactors;と簡単に書けませんlastFactors = factors;か?factorsがローカル変数であり、それが に渡されるという理由だけでencodeIntoResponse、それを変更できますか?

質問が明確であることを願っています。ありがとう。

4

5 に答える 5

2

これを防御的コピーと呼びます。配列は他のオブジェクトと同様にオブジェクトであるため、

 factors = lastFactors

lastFactos の参照を factor に割り当て、その逆も同様です。そのため、誰でも自分の制御外で自分の状態を上書きできます。例として:

private void filterAndRemove(BigInteger[] arr);
private void encodeIntoResponse(..., BigInteger[] factors) {
   filterAndRemove(factors);
}

理論上の割り当てを使用すると、filterAndRemove は元の lastFactorials にも影響します。

于 2012-10-05T11:09:52.757 に答える
0

基本から推測される回答:オブジェクトを変更する予定があり、元のオブジェクトを変更したくない場合は、クローンを作成する必要があります。この場合、変更したくないので、factors = lastFactors.clone();クローンlastFactorsを作成して送信しますencodeIntoResponse(resp, factors);。それを変更するコードが含まれている可能性があります。

于 2012-08-20T08:31:18.970 に答える
0

本のそのセクションは、本の著者によってより適切に説明されている可能性があることに同意します。

スレッド セーフを適切に実装するには、同じロックを使用して読み取り操作と書き込み操作の両方を同期する必要があることは事実です。上記のコードでは、同期の量を最小限に抑えるために、作成者は同期encodeIntoResponse(...)なしで を実行することにしました。このencodeIntoResponse(...)メソッドは、によって参照される配列の内容を読み取るfactorsため、作成者はそれを新しい配列に複製しました。

注: がローカル変数であることは事実ですfactorsが、一般に、同じ配列が同期されたコードと同期されていないコードによって読み取られるため、クローンを作成する必要がlastFactorsありencodeIntoResponse(...)ます。

ただし、質問の@khachikと応答の@david-harknessが正しく指摘しているように、この特定のケースでは、安全に公開され、公開後に変更されcloneないため、呼び出しは不要です。lastFactors

于 2016-10-19T07:47:04.207 に答える
0

配列を複製する唯一の理由は、配列要素の変更を (この場合は同時に) ブロックすることです。ただし、この例では、参照される配列を他のメソッドが変更しないと仮定すると、それは可能ではないように見えますlastFactorsfactorsと に格納されている配列lastFactorsはすべて によって作成され、完全な状態で返されますfactor。それらの参照は同期ブロック内に割り当てられ、安全にパブリッシュされます。

encodeIntoResponse引数の要素を変更しない限り、呼び出しは不要factorsのように見えます。clone

于 2012-10-23T15:04:27.007 に答える