2

面接で以下の質問をされました。

複数のスレッドで共有されているオブジェクトがあります。オブジェクトは以下の機能を持っています。パラメータ x の異なる値に対して、異なるスレッドが関数を同時に実行できるようにするにはどうすればよいでしょうか? 2 つのスレッドが同じ値の x で実行されている場合、そのうちの 1 つをブロックする必要があります。

public void func(String x){
-----
}  

この場合、「synchronized」キーワードは機能しません。一度に実行できるスレッドは 1 つだけであるためです。この場合の解決策を教えてください

4

3 に答える 3

6

最初に頭に浮かぶのは、次のようなものです

public void func(String x){
    synchronized (x.intern()) {
        // Body here
    }
}  

これは説明どおりに動作します。もちろん、同期されているオブジェクトはパブリックにアクセス可能であり、結果として他のコードがロックに干渉する可能性があるため、厄介なハックのように感じます.

于 2013-02-21T05:44:24.080 に答える
0

おそらくインタビュアーは、アーネスト・フリードマン・ヒルによって提案された解決策に興味を持っていました。ただし、その欠点のため、通常、本番コードでは使用できません。この問題を処理するために次の同期ユーティリティを作成したら、次のようにします。

package com.paypal.risk.ars.dss.framework.concurrent;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;


public class MultiLock <K>{
    private ConcurrentHashMap<K, ReentrantLock> locks = new ConcurrentHashMap<K, ReentrantLock>();  

    /**
     * Locks on a given id.
     * Make sure to call unlock() afterwards, otherwise serious bugs may occur.
     * It is strongly recommended to use try{ }finally{} in order to guarantee this.
     * Note that the lock is re-entrant. 
     * @param id The id to lock on
     */
    public void lock(K id) {
        while (true) {
            ReentrantLock lock = getLockFor(id);
            lock.lock();
            if (locks.get(id) == lock)
                return;
            else // means that the lock has been removed from the map by another thread, so it is not safe to
                 // continue with the one we have, and we must retry.
                 // without this, another thread may create a new lock for the same id, and then work on it.
                lock.unlock();
        }
    }

    /**
     * Unlocks on a given id.
     * If the lock is not currently held, an exception is thrown.
     *  
     * @param id The id to unlock
     * @throws IllegalMonitorStateException in case that the thread doesn't hold the lock
     */
    public void unlock(K id) {
        ReentrantLock lock = locks.get(id);
        if (lock == null || !lock.isHeldByCurrentThread())
            throw new IllegalMonitorStateException("Lock for " + id + " is not owned by the current thread!");
        locks.remove(id);
        lock.unlock();
    }

    private ReentrantLock getLockFor(K id) {
        ReentrantLock lock = locks.get(id);
        if (lock == null) {
            lock = new ReentrantLock();
            ReentrantLock prevLock = locks.putIfAbsent(id, lock);
            if (prevLock != null)
                lock = prevLock;
        }
        return lock;
    }
}

単純なマップとグローバルロックを使用して、より単純な方法で実装できることに注意してください。ただし、スループットを向上させるために、グローバルロックを回避したかったのです。

于 2013-02-21T06:01:40.867 に答える
0

メンバー変数として HashMap を作成します。

private HashMap<String,Lock> lockMap = new HashMap<String,Lock>();
public void func(String x){
    if(lockMap.get(x) == null){
        lockMap.put(x,new ReentrantLock());
    }  
    lockMap.get(x).lock(); 
    ...
    ...  
    lockMap.get(x).unlock(); 
}
于 2013-02-21T05:47:55.990 に答える